diff options
Diffstat (limited to 'usr/src')
121 files changed, 18630 insertions, 3519 deletions
diff --git a/usr/src/cmd/devfsadm/i386/misc_link_i386.c b/usr/src/cmd/devfsadm/i386/misc_link_i386.c index ff0b721f29..566d2c88a9 100644 --- a/usr/src/cmd/devfsadm/i386/misc_link_i386.c +++ b/usr/src/cmd/devfsadm/i386/misc_link_i386.c @@ -32,7 +32,7 @@ #include <stdlib.h> #include <limits.h> #include <ctype.h> -#include <sys/mc.h> +#include <sys/mc_amd.h> #include <bsm/devalloc.h> extern int system_labeled; @@ -551,11 +551,17 @@ mc_node(di_minor_t minor, di_node_t node) errno = 0; unitaddr = strtol(busaddr, &c, 16); - if (errno != 0 || unitaddr < MC_AMD_DEV_OFFSET) + if (errno != 0) return (DEVFSADM_CONTINUE); - (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", - unitaddr - MC_AMD_DEV_OFFSET); + if (unitaddr == 0) { + (void) snprintf(linkpath, sizeof (linkpath), "mc/mc"); + } else if (unitaddr >= MC_AMD_DEV_OFFSET) { + (void) snprintf(linkpath, sizeof (linkpath), "mc/mc%u", + unitaddr - MC_AMD_DEV_OFFSET); + } else { + return (DEVFSADM_CONTINUE); + } (void) devfsadm_mklink(linkpath, node, minor, 0); return (DEVFSADM_CONTINUE); } diff --git a/usr/src/cmd/fm/dicts/GMCA.dict b/usr/src/cmd/fm/dicts/GMCA.dict new file mode 100644 index 0000000000..ab5fdc1a46 --- /dev/null +++ b/usr/src/cmd/fm/dicts/GMCA.dict @@ -0,0 +1,62 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# DO NOT EDIT -- this file is generated by the Event Registry. +# + +FMDICT: name=GMCA version=1 maxkey=1 dictid=0x4D43 + +fault.cpu.generic-x86.internal=1 +fault.cpu.generic-x86.l0cache=2 +fault.cpu.generic-x86.l1cache=3 +fault.cpu.generic-x86.l2cache=4 +fault.cpu.generic-x86.cache=5 +fault.cpu.generic-x86.l0dtlb=6 +fault.cpu.generic-x86.l1dtlb=7 +fault.cpu.generic-x86.l2dtlb=8 +fault.cpu.generic-x86.dtlb=9 +fault.cpu.generic-x86.l0itlb=10 +fault.cpu.generic-x86.l1itlb=11 +fault.cpu.generic-x86.l2itlb=12 +fault.cpu.generic-x86.itlb=13 +fault.cpu.generic-x86.l0tlb=14 +fault.cpu.generic-x86.l1tlb=15 +fault.cpu.generic-x86.l2tlb=16 +fault.cpu.generic-x86.tlb=17 +fault.cpu.generic-x86.l0dcache=18 +fault.cpu.generic-x86.l1dcache=19 +fault.cpu.generic-x86.l2dcache=20 +fault.cpu.generic-x86.dcache=21 +fault.cpu.generic-x86.l0icache=22 +fault.cpu.generic-x86.l1icache=23 +fault.cpu.generic-x86.l2icache=24 +fault.cpu.generic-x86.icache=25 +fault.cpu.generic-x86.bus_interconnect=26 +fault.cpu.generic-x86.bus_interconnect_memory=27 +fault.cpu.generic-x86.bus_interconnect_io=28 +fault.memory.generic-x86.page_ce=29 +fault.memory.generic-x86.page_ue=30 +fault.memory.generic-x86.dimm_ce=31 +fault.memory.generic-x86.dimm_ue=32 diff --git a/usr/src/cmd/fm/dicts/GMCA.po b/usr/src/cmd/fm/dicts/GMCA.po new file mode 100644 index 0000000000..a6e359d3c4 --- /dev/null +++ b/usr/src/cmd/fm/dicts/GMCA.po @@ -0,0 +1,539 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# DO NOT EDIT -- this file is generated by the Event Registry. +# +# +# code: GMCA-8000-1K +# keys: fault.cpu.generic-x86.internal +# +msgid "GMCA-8000-1K.type" +msgstr "Fault" +msgid "GMCA-8000-1K.severity" +msgstr "Major" +msgid "GMCA-8000-1K.description" +msgstr "An internal error has been encountered on this cpu. Refer to %s for more information." +msgid "GMCA-8000-1K.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-1K.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-1K.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-21 +# keys: fault.cpu.generic-x86.l0cache +# +msgid "GMCA-8000-21.type" +msgstr "Fault" +msgid "GMCA-8000-21.severity" +msgstr "Major" +msgid "GMCA-8000-21.description" +msgstr "A level 0 cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-21.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-21.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-21.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-3W +# keys: fault.cpu.generic-x86.l1cache +# +msgid "GMCA-8000-3W.type" +msgstr "Fault" +msgid "GMCA-8000-3W.severity" +msgstr "Major" +msgid "GMCA-8000-3W.description" +msgstr "A level 1 cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-3W.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-3W.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-3W.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-47 +# keys: fault.cpu.generic-x86.l2cache +# +msgid "GMCA-8000-47.type" +msgstr "Fault" +msgid "GMCA-8000-47.severity" +msgstr "Major" +msgid "GMCA-8000-47.description" +msgstr "A level 2 cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-47.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-47.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-47.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-5U +# keys: fault.cpu.generic-x86.cache +# +msgid "GMCA-8000-5U.type" +msgstr "Fault" +msgid "GMCA-8000-5U.severity" +msgstr "Major" +msgid "GMCA-8000-5U.description" +msgstr "A cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-5U.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-5U.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-5U.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-68 +# keys: fault.cpu.generic-x86.l0dtlb +# +msgid "GMCA-8000-68.type" +msgstr "Fault" +msgid "GMCA-8000-68.severity" +msgstr "Major" +msgid "GMCA-8000-68.description" +msgstr "A level 0 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-68.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-68.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-68.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-7M +# keys: fault.cpu.generic-x86.l1dtlb +# +msgid "GMCA-8000-7M.type" +msgstr "Fault" +msgid "GMCA-8000-7M.severity" +msgstr "Major" +msgid "GMCA-8000-7M.description" +msgstr "A level 1 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-7M.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-7M.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-7M.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-8V +# keys: fault.cpu.generic-x86.l2dtlb +# +msgid "GMCA-8000-8V.type" +msgstr "Fault" +msgid "GMCA-8000-8V.severity" +msgstr "Major" +msgid "GMCA-8000-8V.description" +msgstr "A level 2 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-8V.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-8V.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-8V.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-90 +# keys: fault.cpu.generic-x86.dtlb +# +msgid "GMCA-8000-90.type" +msgstr "Fault" +msgid "GMCA-8000-90.severity" +msgstr "Major" +msgid "GMCA-8000-90.description" +msgstr "A Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-90.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-90.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-90.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-AL +# keys: fault.cpu.generic-x86.l0itlb +# +msgid "GMCA-8000-AL.type" +msgstr "Fault" +msgid "GMCA-8000-AL.severity" +msgstr "Major" +msgid "GMCA-8000-AL.description" +msgstr "A level 0 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-AL.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-AL.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-AL.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-CG +# keys: fault.cpu.generic-x86.l1itlb +# +msgid "GMCA-8000-CG.type" +msgstr "Fault" +msgid "GMCA-8000-CG.severity" +msgstr "Major" +msgid "GMCA-8000-CG.description" +msgstr "A level 1 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-CG.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-CG.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-CG.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-DN +# keys: fault.cpu.generic-x86.l2itlb +# +msgid "GMCA-8000-DN.type" +msgstr "Fault" +msgid "GMCA-8000-DN.severity" +msgstr "Major" +msgid "GMCA-8000-DN.description" +msgstr "A level 2 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-DN.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-DN.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-DN.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-E9 +# keys: fault.cpu.generic-x86.itlb +# +msgid "GMCA-8000-E9.type" +msgstr "Fault" +msgid "GMCA-8000-E9.severity" +msgstr "Major" +msgid "GMCA-8000-E9.description" +msgstr "An Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-E9.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-E9.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-E9.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-FT +# keys: fault.cpu.generic-x86.l0tlb +# +msgid "GMCA-8000-FT.type" +msgstr "Fault" +msgid "GMCA-8000-FT.severity" +msgstr "Major" +msgid "GMCA-8000-FT.description" +msgstr "A level 0 TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-FT.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-FT.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-FT.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-G6 +# keys: fault.cpu.generic-x86.l1tlb +# +msgid "GMCA-8000-G6.type" +msgstr "Fault" +msgid "GMCA-8000-G6.severity" +msgstr "Major" +msgid "GMCA-8000-G6.description" +msgstr "A level 1 TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-G6.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-G6.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-G6.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-HW +# keys: fault.cpu.generic-x86.l2tlb +# +msgid "GMCA-8000-HW.type" +msgstr "Fault" +msgid "GMCA-8000-HW.severity" +msgstr "Major" +msgid "GMCA-8000-HW.description" +msgstr "A level 2 TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-HW.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-HW.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-HW.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-J1 +# keys: fault.cpu.generic-x86.tlb +# +msgid "GMCA-8000-J1.type" +msgstr "Fault" +msgid "GMCA-8000-J1.severity" +msgstr "Major" +msgid "GMCA-8000-J1.description" +msgstr "A TLB on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-J1.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-J1.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-J1.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-KK +# keys: fault.cpu.generic-x86.l0dcache +# +msgid "GMCA-8000-KK.type" +msgstr "Fault" +msgid "GMCA-8000-KK.severity" +msgstr "Major" +msgid "GMCA-8000-KK.description" +msgstr "A level 0 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-KK.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-KK.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-KK.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-LF +# keys: fault.cpu.generic-x86.l1dcache +# +msgid "GMCA-8000-LF.type" +msgstr "Fault" +msgid "GMCA-8000-LF.severity" +msgstr "Major" +msgid "GMCA-8000-LF.description" +msgstr "A level 1 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-LF.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-LF.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-LF.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-MM +# keys: fault.cpu.generic-x86.l2dcache +# +msgid "GMCA-8000-MM.type" +msgstr "Fault" +msgid "GMCA-8000-MM.severity" +msgstr "Major" +msgid "GMCA-8000-MM.description" +msgstr "A level 2 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-MM.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-MM.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-MM.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-N8 +# keys: fault.cpu.generic-x86.dcache +# +msgid "GMCA-8000-N8.type" +msgstr "Fault" +msgid "GMCA-8000-N8.severity" +msgstr "Major" +msgid "GMCA-8000-N8.description" +msgstr "A Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-N8.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-N8.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-N8.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-PU +# keys: fault.cpu.generic-x86.l0icache +# +msgid "GMCA-8000-PU.type" +msgstr "Fault" +msgid "GMCA-8000-PU.severity" +msgstr "Major" +msgid "GMCA-8000-PU.description" +msgstr "A level 0 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-PU.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-PU.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-PU.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-Q7 +# keys: fault.cpu.generic-x86.l1icache +# +msgid "GMCA-8000-Q7.type" +msgstr "Fault" +msgid "GMCA-8000-Q7.severity" +msgstr "Major" +msgid "GMCA-8000-Q7.description" +msgstr "A level 1 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-Q7.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-Q7.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-Q7.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-RG +# keys: fault.cpu.generic-x86.l2icache +# +msgid "GMCA-8000-RG.type" +msgstr "Fault" +msgid "GMCA-8000-RG.severity" +msgstr "Major" +msgid "GMCA-8000-RG.description" +msgstr "A level 2 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-RG.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-RG.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-RG.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-SL +# keys: fault.cpu.generic-x86.icache +# +msgid "GMCA-8000-SL.type" +msgstr "Fault" +msgid "GMCA-8000-SL.severity" +msgstr "Major" +msgid "GMCA-8000-SL.description" +msgstr "An Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "GMCA-8000-SL.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "GMCA-8000-SL.impact" +msgstr "Performance of this system may be affected." +msgid "GMCA-8000-SL.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: GMCA-8000-T0 +# keys: fault.cpu.generic-x86.bus_interconnect +# +msgid "GMCA-8000-T0.type" +msgstr "Fault" +msgid "GMCA-8000-T0.severity" +msgstr "Major" +msgid "GMCA-8000-T0.description" +msgstr "Errors reported by a bus or interconnect (typically IO or memory errors) have exceeded acceptable levels Refer to %s for more information." +msgid "GMCA-8000-T0.response" +msgstr "No automated response action is possible" +msgid "GMCA-8000-T0.impact" +msgstr "None (no automated response)" +msgid "GMCA-8000-T0.action" +msgstr "Additional manual investigation is required to determine the root cause of the fault. Use 'fmadm faulty' to see which chip *detected* the errors (it is likely not the cause of them)." +# +# code: GMCA-8000-UV +# keys: fault.cpu.generic-x86.bus_interconnect_memory +# +msgid "GMCA-8000-UV.type" +msgstr "Fault" +msgid "GMCA-8000-UV.severity" +msgstr "Major" +msgid "GMCA-8000-UV.description" +msgstr "Errors reported by a bus or interconnect for memory accesses have exceeded acceptable levels Refer to %s for more information." +msgid "GMCA-8000-UV.response" +msgstr "No automated response action is possible" +msgid "GMCA-8000-UV.impact" +msgstr "None (no automated response)" +msgid "GMCA-8000-UV.action" +msgstr "Additional manual investigation is required to determine the root cause of the fault. Use 'fmadm faulty' to see which chip *detected* the errors (it is likely not the cause of them)." +# +# code: GMCA-8000-V6 +# keys: fault.cpu.generic-x86.bus_interconnect_io +# +msgid "GMCA-8000-V6.type" +msgstr "Fault" +msgid "GMCA-8000-V6.severity" +msgstr "Major" +msgid "GMCA-8000-V6.description" +msgstr "Errors reported by a bus or interconnect for IO accesses have exceeded acceptable levels Refer to %s for more information." +msgid "GMCA-8000-V6.response" +msgstr "No automated response action is possible" +msgid "GMCA-8000-V6.impact" +msgstr "None (no automated response)" +msgid "GMCA-8000-V6.action" +msgstr "Additional manual investigation is required to determine the root cause of the fault. Use 'fmadm faulty' to see which chip *detected* the errors (it is likely not the cause of them)." +# +# code: GMCA-8000-WT +# keys: fault.memory.generic-x86.page_ce +# +msgid "GMCA-8000-WT.type" +msgstr "Fault" +msgid "GMCA-8000-WT.severity" +msgstr "Minor" +msgid "GMCA-8000-WT.description" +msgstr "A page of memory is reporting excessive ECC errors. Refer to %s for more information." +msgid "GMCA-8000-WT.response" +msgstr "The system will attempt to retire the affected page from further use." +msgid "GMCA-8000-WT.impact" +msgstr "Retiring a single page from use will have negligible performance impact." +msgid "GMCA-8000-WT.action" +msgstr "No service or repair action is required; a separate dimm fault will be diagnosed if appropriate." +# +# code: GMCA-8000-X9 +# keys: fault.memory.generic-x86.page_ue +# +msgid "GMCA-8000-X9.type" +msgstr "Fault" +msgid "GMCA-8000-X9.severity" +msgstr "Major" +msgid "GMCA-8000-X9.description" +msgstr "An uncorrectable error was reported on a memory page. Refer to %s for more information." +msgid "GMCA-8000-X9.response" +msgstr "The system will attempt to retire the affected page from further use." +msgid "GMCA-8000-X9.impact" +msgstr "Retiring a single page from use will have negligible performance impact." +msgid "GMCA-8000-X9.action" +msgstr "This is an internal fault; a repair action will be indicated by an accompanying dimm fault diagnosis." +# +# code: GMCA-8000-YN +# keys: fault.memory.generic-x86.dimm_ce +# +msgid "GMCA-8000-YN.type" +msgstr "Fault" +msgid "GMCA-8000-YN.severity" +msgstr "Major" +msgid "GMCA-8000-YN.description" +msgstr "A memory module is experiencing excessive correctable errors affecting large numbers of pages. Refer to %s for more information." +msgid "GMCA-8000-YN.response" +msgstr "Affected pages will be retired up to a limit." +msgid "GMCA-8000-YN.impact" +msgstr "Page retirements are capped at a small fraction of memory to avoid performance impact." +msgid "GMCA-8000-YN.action" +msgstr "Schedule a repair action to replace the memory module indicated by 'fmadm faulty'. While the errors are correctable in nature they may be a precursor to an uncorrectable error which will result in downtime." +# +# code: GMCA-8001-09 +# keys: fault.memory.generic-x86.dimm_ue +# +msgid "GMCA-8001-09.type" +msgstr "Fault" +msgid "GMCA-8001-09.severity" +msgstr "Critical" +msgid "GMCA-8001-09.description" +msgstr "A memory module has suffered an uncorrectable error. Refer to %s for more information." +msgid "GMCA-8001-09.response" +msgstr "The affected page will be retired from further use." +msgid "GMCA-8001-09.impact" +msgstr "Retiring a single page from use will have negligible performance impact." +msgid "GMCA-8001-09.action" +msgstr "Schedule an urgent repair action to replace the memory module indicated by 'fmadm faulty'." diff --git a/usr/src/cmd/fm/dicts/INTEL.dict b/usr/src/cmd/fm/dicts/INTEL.dict new file mode 100644 index 0000000000..18032bb0da --- /dev/null +++ b/usr/src/cmd/fm/dicts/INTEL.dict @@ -0,0 +1,72 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# DO NOT EDIT -- this file is generated by the Event Registry. +# + +FMDICT: name=INTEL version=1 maxkey=1 dictid=0x494e + +fault.cpu.intel.internal=1 +fault.cpu.intel.l0cache=2 +fault.cpu.intel.l1cache=3 +fault.cpu.intel.l2cache=4 +fault.cpu.intel.cache=5 +fault.cpu.intel.l0dtlb=6 +fault.cpu.intel.l1dtlb=7 +fault.cpu.intel.l2dtlb=8 +fault.cpu.intel.dtlb=9 +fault.cpu.intel.l0itlb=10 +fault.cpu.intel.l1itlb=11 +fault.cpu.intel.l2itlb=12 +fault.cpu.intel.itlb=13 +fault.cpu.intel.l0tlb=14 +fault.cpu.intel.l1tlb=15 +fault.cpu.intel.l2tlb=16 +fault.cpu.intel.tlb=17 +fault.cpu.intel.l0dcache=18 +fault.cpu.intel.l1dcache=19 +fault.cpu.intel.l2dcache=20 +fault.cpu.intel.dcache=21 +fault.cpu.intel.l0icache=22 +fault.cpu.intel.l1icache=23 +fault.cpu.intel.l2icache=24 +fault.cpu.intel.icache=25 +fault.cpu.intel.bus_interconnect=26 +fault.cpu.intel.bus_interconnect_memory=27 +fault.cpu.intel.bus_interconnect_io=28 +fault.cpu.intel.nb.fsb=29 +fault.memory.intel.fbd.berr=30 +fault.memory.intel.fbd.alert=31 +fault.memory.intel.fbd.crc=32 +fault.memory.intel.fbd.ch=33 +fault.memory.intel.fbd.reset_timeout=34 +fault.memory.intel.fbd.otf=35 +fault.cpu.intel.nb.ie=36 +fault.cpu.intel.nb.dma=37 +fault.memory.intel.fbd.mem_ds=38 +fault.memory.intel.page_ce=39 +fault.memory.intel.page_ue=40 +fault.memory.intel.dimm_ce=41 +fault.memory.intel.dimm_ue=42 diff --git a/usr/src/cmd/fm/dicts/INTEL.po b/usr/src/cmd/fm/dicts/INTEL.po new file mode 100644 index 0000000000..5c8d6b9a5b --- /dev/null +++ b/usr/src/cmd/fm/dicts/INTEL.po @@ -0,0 +1,699 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# DO NOT EDIT -- this file is generated by the Event Registry. +# +# +# code: INTEL-8000-1J +# keys: fault.cpu.intel.internal +# +msgid "INTEL-8000-1J.type" +msgstr "Fault" +msgid "INTEL-8000-1J.severity" +msgstr "Major" +msgid "INTEL-8000-1J.description" +msgstr "An internal error has been encountered on this cpu. Refer to %s for more information." +msgid "INTEL-8000-1J.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-1J.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-1J.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-22 +# keys: fault.cpu.intel.l0cache +# +msgid "INTEL-8000-22.type" +msgstr "Fault" +msgid "INTEL-8000-22.severity" +msgstr "Major" +msgid "INTEL-8000-22.description" +msgstr "A level 0 cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-22.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-22.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-22.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-3X +# keys: fault.cpu.intel.l1cache +# +msgid "INTEL-8000-3X.type" +msgstr "Fault" +msgid "INTEL-8000-3X.severity" +msgstr "Major" +msgid "INTEL-8000-3X.description" +msgstr "A level 1 cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-3X.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-3X.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-3X.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-44 +# keys: fault.cpu.intel.l2cache +# +msgid "INTEL-8000-44.type" +msgstr "Fault" +msgid "INTEL-8000-44.severity" +msgstr "Major" +msgid "INTEL-8000-44.description" +msgstr "A level 2 cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-44.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-44.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-44.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-5R +# keys: fault.cpu.intel.cache +# +msgid "INTEL-8000-5R.type" +msgstr "Fault" +msgid "INTEL-8000-5R.severity" +msgstr "Major" +msgid "INTEL-8000-5R.description" +msgstr "A cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-5R.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-5R.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-5R.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-6C +# keys: fault.cpu.intel.l0dtlb +# +msgid "INTEL-8000-6C.type" +msgstr "Fault" +msgid "INTEL-8000-6C.severity" +msgstr "Major" +msgid "INTEL-8000-6C.description" +msgstr "A level 0 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-6C.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-6C.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-6C.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-7Q +# keys: fault.cpu.intel.l1dtlb +# +msgid "INTEL-8000-7Q.type" +msgstr "Fault" +msgid "INTEL-8000-7Q.severity" +msgstr "Major" +msgid "INTEL-8000-7Q.description" +msgstr "A level 1 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-7Q.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-7Q.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-7Q.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-8Y +# keys: fault.cpu.intel.l2dtlb +# +msgid "INTEL-8000-8Y.type" +msgstr "Fault" +msgid "INTEL-8000-8Y.severity" +msgstr "Major" +msgid "INTEL-8000-8Y.description" +msgstr "A level 2 Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-8Y.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-8Y.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-8Y.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-93 +# keys: fault.cpu.intel.dtlb +# +msgid "INTEL-8000-93.type" +msgstr "Fault" +msgid "INTEL-8000-93.severity" +msgstr "Major" +msgid "INTEL-8000-93.description" +msgstr "A Data TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-93.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-93.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-93.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-AH +# keys: fault.cpu.intel.l0itlb +# +msgid "INTEL-8000-AH.type" +msgstr "Fault" +msgid "INTEL-8000-AH.severity" +msgstr "Major" +msgid "INTEL-8000-AH.description" +msgstr "A level 0 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-AH.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-AH.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-AH.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-CD +# keys: fault.cpu.intel.l1itlb +# +msgid "INTEL-8000-CD.type" +msgstr "Fault" +msgid "INTEL-8000-CD.severity" +msgstr "Major" +msgid "INTEL-8000-CD.description" +msgstr "A level 1 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-CD.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-CD.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-CD.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-DP +# keys: fault.cpu.intel.l2itlb +# +msgid "INTEL-8000-DP.type" +msgstr "Fault" +msgid "INTEL-8000-DP.severity" +msgstr "Major" +msgid "INTEL-8000-DP.description" +msgstr "A level 2 Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-DP.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-DP.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-DP.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-EA +# keys: fault.cpu.intel.itlb +# +msgid "INTEL-8000-EA.type" +msgstr "Fault" +msgid "INTEL-8000-EA.severity" +msgstr "Major" +msgid "INTEL-8000-EA.description" +msgstr "An Instruction TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-EA.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-EA.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-EA.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-FS +# keys: fault.cpu.intel.l0tlb +# +msgid "INTEL-8000-FS.type" +msgstr "Fault" +msgid "INTEL-8000-FS.severity" +msgstr "Major" +msgid "INTEL-8000-FS.description" +msgstr "A level 0 TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-FS.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-FS.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-FS.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-G5 +# keys: fault.cpu.intel.l1tlb +# +msgid "INTEL-8000-G5.type" +msgstr "Fault" +msgid "INTEL-8000-G5.severity" +msgstr "Major" +msgid "INTEL-8000-G5.description" +msgstr "A level 1 TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-G5.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-G5.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-G5.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-HX +# keys: fault.cpu.intel.l2tlb +# +msgid "INTEL-8000-HX.type" +msgstr "Fault" +msgid "INTEL-8000-HX.severity" +msgstr "Major" +msgid "INTEL-8000-HX.description" +msgstr "A level 2 TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-HX.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-HX.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-HX.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-J2 +# keys: fault.cpu.intel.tlb +# +msgid "INTEL-8000-J2.type" +msgstr "Fault" +msgid "INTEL-8000-J2.severity" +msgstr "Major" +msgid "INTEL-8000-J2.description" +msgstr "A TLB on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-J2.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-J2.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-J2.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-KJ +# keys: fault.cpu.intel.l0dcache +# +msgid "INTEL-8000-KJ.type" +msgstr "Fault" +msgid "INTEL-8000-KJ.severity" +msgstr "Major" +msgid "INTEL-8000-KJ.description" +msgstr "A level 0 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-KJ.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-KJ.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-KJ.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-LE +# keys: fault.cpu.intel.l1dcache +# +msgid "INTEL-8000-LE.type" +msgstr "Fault" +msgid "INTEL-8000-LE.severity" +msgstr "Major" +msgid "INTEL-8000-LE.description" +msgstr "A level 1 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-LE.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-LE.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-LE.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-MQ +# keys: fault.cpu.intel.l2dcache +# +msgid "INTEL-8000-MQ.type" +msgstr "Fault" +msgid "INTEL-8000-MQ.severity" +msgstr "Major" +msgid "INTEL-8000-MQ.description" +msgstr "A level 2 Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-MQ.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-MQ.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-MQ.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-NC +# keys: fault.cpu.intel.dcache +# +msgid "INTEL-8000-NC.type" +msgstr "Fault" +msgid "INTEL-8000-NC.severity" +msgstr "Major" +msgid "INTEL-8000-NC.description" +msgstr "A Data Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-NC.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-NC.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-NC.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-PR +# keys: fault.cpu.intel.l0icache +# +msgid "INTEL-8000-PR.type" +msgstr "Fault" +msgid "INTEL-8000-PR.severity" +msgstr "Major" +msgid "INTEL-8000-PR.description" +msgstr "A level 0 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-PR.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-PR.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-PR.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-Q4 +# keys: fault.cpu.intel.l1icache +# +msgid "INTEL-8000-Q4.type" +msgstr "Fault" +msgid "INTEL-8000-Q4.severity" +msgstr "Major" +msgid "INTEL-8000-Q4.description" +msgstr "A level 1 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-Q4.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-Q4.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-Q4.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-RD +# keys: fault.cpu.intel.l2icache +# +msgid "INTEL-8000-RD.type" +msgstr "Fault" +msgid "INTEL-8000-RD.severity" +msgstr "Major" +msgid "INTEL-8000-RD.description" +msgstr "A level 2 Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-RD.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-RD.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-RD.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-SH +# keys: fault.cpu.intel.icache +# +msgid "INTEL-8000-SH.type" +msgstr "Fault" +msgid "INTEL-8000-SH.severity" +msgstr "Major" +msgid "INTEL-8000-SH.description" +msgstr "An Instruction Cache on this cpu is faulty. Refer to %s for more information." +msgid "INTEL-8000-SH.response" +msgstr "The system will attempt to offline this cpu to remove it from service." +msgid "INTEL-8000-SH.impact" +msgstr "Performance of this system may be affected." +msgid "INTEL-8000-SH.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-T3 +# keys: fault.cpu.intel.bus_interconnect +# +msgid "INTEL-8000-T3.type" +msgstr "Fault" +msgid "INTEL-8000-T3.severity" +msgstr "Major" +msgid "INTEL-8000-T3.description" +msgstr "Excessive errors from a bus, memory or IO. Refer to %s for more information. Refer to %s for more information." +msgid "INTEL-8000-T3.response" +msgstr "No automated response action is possible" +msgid "INTEL-8000-T3.impact" +msgstr "System may panic or be reset by BIOS" +msgid "INTEL-8000-T3.action" +msgstr "Schedule a repair procedure to check seating of memory dimms. Schedule a repair procedure to check seating of io cards" +# +# code: INTEL-8000-UY +# keys: fault.cpu.intel.bus_interconnect_memory +# +msgid "INTEL-8000-UY.type" +msgstr "Fault" +msgid "INTEL-8000-UY.severity" +msgstr "Major" +msgid "INTEL-8000-UY.description" +msgstr "Excessive errors from memory accesses. Refer to %s for more information." +msgid "INTEL-8000-UY.response" +msgstr "No automated response action is possible" +msgid "INTEL-8000-UY.impact" +msgstr "System may panic or be reset by BIOS" +msgid "INTEL-8000-UY.action" +msgstr "Schedule a repair procedure to reseat or replace memory dimms" +# +# code: INTEL-8000-V5 +# keys: fault.cpu.intel.bus_interconnect_io +# +msgid "INTEL-8000-V5.type" +msgstr "Fault" +msgid "INTEL-8000-V5.severity" +msgstr "Major" +msgid "INTEL-8000-V5.description" +msgstr "Excessive errors from IO accesses. Refer to %s for more information." +msgid "INTEL-8000-V5.response" +msgstr "No automated response action is possible" +msgid "INTEL-8000-V5.impact" +msgstr "System may panic or be reset by BIOS" +msgid "INTEL-8000-V5.action" +msgstr "Schedule a repair procedure to check seating of io cards" +# +# code: INTEL-8000-WS +# keys: fault.cpu.intel.nb.fsb +# +msgid "INTEL-8000-WS.type" +msgstr "Fault" +msgid "INTEL-8000-WS.severity" +msgstr "Critical" +msgid "INTEL-8000-WS.description" +msgstr "Front Side bus between CPU and northbridge Refer to %s for more information." +msgid "INTEL-8000-WS.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8000-WS.impact" +msgstr "System may panic or be reset by BIOS" +msgid "INTEL-8000-WS.action" +msgstr "Schedule a repair procedure to replace the affected CPU. Use 'fmadm faulty' to identify the module." +# +# code: INTEL-8000-XA +# keys: fault.memory.intel.fbd.berr +# +msgid "INTEL-8000-XA.type" +msgstr "Fault" +msgid "INTEL-8000-XA.severity" +msgstr "Major" +msgid "INTEL-8000-XA.description" +msgstr "A fault occurred on a memory dimm channel Refer to %s for more information." +msgid "INTEL-8000-XA.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8000-XA.impact" +msgstr "System may not boot" +msgid "INTEL-8000-XA.action" +msgstr "reseat or replace dimms on channel. Use 'fmadm faulty' to identify the channel." +# +# code: INTEL-8000-YP +# keys: fault.memory.intel.fbd.alert +# +msgid "INTEL-8000-YP.type" +msgstr "Fault" +msgid "INTEL-8000-YP.severity" +msgstr "Critical" +msgid "INTEL-8000-YP.description" +msgstr "Memory Controller detected a corrupted acknowledgment on a retry of a non-redundant memory write or configuration write Refer to %s for more information." +msgid "INTEL-8000-YP.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8000-YP.impact" +msgstr "System may be unexpectedly reset" +msgid "INTEL-8000-YP.action" +msgstr "reseat or replace dimm. Use 'fmadm faulty' to identify the dimm." +# +# code: INTEL-8001-0A +# keys: fault.memory.intel.fbd.crc +# +msgid "INTEL-8001-0A.type" +msgstr "Fault" +msgid "INTEL-8001-0A.severity" +msgstr "Critical" +msgid "INTEL-8001-0A.description" +msgstr "Memory Controller detected a corrupted CRC error on a non-redundant retry of memory or dimm configuration read. Refer to %s for more information." +msgid "INTEL-8001-0A.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8001-0A.impact" +msgstr "System may not boot" +msgid "INTEL-8001-0A.action" +msgstr "reseat or replace dimm. Use 'fmadm faulty' to identify the dimm." +# +# code: INTEL-8001-1P +# keys: fault.memory.intel.fbd.ch +# +msgid "INTEL-8001-1P.type" +msgstr "Fault" +msgid "INTEL-8001-1P.severity" +msgstr "Major" +msgid "INTEL-8001-1P.description" +msgstr "Memory Controller detected a corrupted CRC error on a non-redundant retry of memory or dimm configuration read Refer to %s for more information." +msgid "INTEL-8001-1P.response" +msgstr "Hardware redundancy or correction of memory errors may be lost" +msgid "INTEL-8001-1P.impact" +msgstr "There may be a loss of hardware redundancy or performance" +msgid "INTEL-8001-1P.action" +msgstr "reseat or replace dimm. Use 'fmadm faulty' to identify the dimm." +# +# code: INTEL-8001-25 +# keys: fault.memory.intel.fbd.reset_timeout +# +msgid "INTEL-8001-25.type" +msgstr "Fault" +msgid "INTEL-8001-25.severity" +msgstr "Critical" +msgid "INTEL-8001-25.description" +msgstr "Memory Controller detected fast reset timeout while trying to recover from read or write Refer to %s for more information." +msgid "INTEL-8001-25.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8001-25.impact" +msgstr "System may not boot" +msgid "INTEL-8001-25.action" +msgstr "reseat or replace dimm. Use 'fmadm faulty' to identify the dimms." +# +# code: INTEL-8001-3S +# keys: fault.memory.intel.fbd.otf +# +msgid "INTEL-8001-3S.type" +msgstr "Fault" +msgid "INTEL-8001-3S.severity" +msgstr "Major" +msgid "INTEL-8001-3S.description" +msgstr "Intelligent throttling is disabled in the memory controller and the thermal sensor detected over temperature Refer to %s for more information." +msgid "INTEL-8001-3S.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8001-3S.impact" +msgstr "System may be unexpectedly reset" +msgid "INTEL-8001-3S.action" +msgstr "Enable intelligent throttling in BIOS or supply more cooling" +# +# code: INTEL-8001-43 +# keys: fault.cpu.intel.nb.ie +# +msgid "INTEL-8001-43.type" +msgstr "Fault" +msgid "INTEL-8001-43.severity" +msgstr "Critical" +msgid "INTEL-8001-43.description" +msgstr "Northbridge has detected an internal error Refer to %s for more information." +msgid "INTEL-8001-43.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8001-43.impact" +msgstr "System may be unexpectedly reset" +msgid "INTEL-8001-43.action" +msgstr "Replace motherboard" +# +# code: INTEL-8001-5Y +# keys: fault.cpu.intel.nb.dma +# +msgid "INTEL-8001-5Y.type" +msgstr "Fault" +msgid "INTEL-8001-5Y.severity" +msgstr "Major" +msgid "INTEL-8001-5Y.description" +msgstr "Northbridge dma controller has detected an error Refer to %s for more information." +msgid "INTEL-8001-5Y.response" +msgstr "System panic or reset by BIOS" +msgid "INTEL-8001-5Y.impact" +msgstr "System may be unexpectedly reset" +msgid "INTEL-8001-5Y.action" +msgstr "Replace motherboard" +# +# code: INTEL-8001-6D +# keys: fault.memory.intel.fbd.mem_ds +# +msgid "INTEL-8001-6D.type" +msgstr "Fault" +msgid "INTEL-8001-6D.severity" +msgstr "Major" +msgid "INTEL-8001-6D.description" +msgstr "The memory controller has deployed the spare dimm rank to replace a rank which has had too many errors Refer to %s for more information." +msgid "INTEL-8001-6D.response" +msgstr "There is no spare rank for this memory controller" +msgid "INTEL-8001-6D.impact" +msgstr "There is a loss of hardware redundancy" +msgid "INTEL-8001-6D.action" +msgstr "Replace faulty dimm. Use 'fmadm faulty' to identify the dimm." +# +# code: INTEL-8001-7H +# keys: fault.memory.intel.page_ce +# +msgid "INTEL-8001-7H.type" +msgstr "Fault" +msgid "INTEL-8001-7H.severity" +msgstr "Minor" +msgid "INTEL-8001-7H.description" +msgstr "This message indicates that the Solaris Fault Manager has received reports that correctable memory errors attributed to a single page of physical memory have been detected. Ongoing diagnosis applied to the error reports has determined that a page is faulty. No repair action is required or recommended at this time. No data has been lost. If and when the Solaris Fault Manager determines that there is sufficient cause, service action will be recommended at that time. Refer to %s for more information." +msgid "INTEL-8001-7H.response" +msgstr "An attempt will be made to remove this memory page from service." +msgid "INTEL-8001-7H.impact" +msgstr "The performance of the system may be minimally impacted as a result of removing the memory page from operation." +msgid "INTEL-8001-7H.action" +msgstr "No repair action is recommended at this time." +# +# code: INTEL-8001-8R +# keys: fault.memory.intel.page_ue +# +msgid "INTEL-8001-8R.type" +msgstr "Fault" +msgid "INTEL-8001-8R.severity" +msgstr "Major" +msgid "INTEL-8001-8R.description" +msgstr "A page has been retired for uncorrectable multiple-bit errors. This fault class is an internal implementation peculiarity and can be ignored by system administrators; action is only necessary if it is followed by a related dimm fault diagnosis (as should always be the case for uncorrectable errors). Refer to %s for more information." +msgid "INTEL-8001-8R.response" +msgstr "An attempt will be made to remove this memory page from service." +msgid "INTEL-8001-8R.impact" +msgstr "The performance of the system may be minimally impacted as a result of removing the memory page from operation." +msgid "INTEL-8001-8R.action" +msgstr "No repair action is recommended at this time." +# +# code: INTEL-8001-94 +# keys: fault.memory.intel.dimm_ce +# +msgid "INTEL-8001-94.type" +msgstr "Fault" +msgid "INTEL-8001-94.severity" +msgstr "Minor" +msgid "INTEL-8001-94.description" +msgstr "This message indicates that the Solaris Fault Manager has received reports of single bit correctable errors from a Memory Module at a rate exceeding acceptable levels, and a Memory Module fault has been diagnosed. No data has been lost, and pages of the affected Memory Module are being retired as errors are encountered. The recommended service action for this event is to schedule replacement of the affected Memory Module at the earliest possible convenience. The errors are correctable in nature so they do not present an immediate threat to system availability, however they may be an indication of an impending uncorrectable failure mode. Use 'fmadm faulty' to identify the dimm to replace. Refer to %s for more information." +msgid "INTEL-8001-94.response" +msgstr "Pages of memory associated with this memory module are being removed from service as errors are reported." +msgid "INTEL-8001-94.impact" +msgstr "Total system memory capacity will be reduced as pages are retired." +msgid "INTEL-8001-94.action" +msgstr "Schedule a repair procedure to replace the affected memory module. Use fmadm faulty -u to identify the module." +# +# code: INTEL-8001-AQ +# keys: fault.memory.intel.dimm_ue +# +msgid "INTEL-8001-AQ.type" +msgstr "Fault" +msgid "INTEL-8001-AQ.severity" +msgstr "Critical" +msgid "INTEL-8001-AQ.description" +msgstr "This message indicates that the Solaris Fault Manager has received reports of Uncorrectable Multiple-Bit memory errors associated with a Memory Module have been detected, and a Memory Module fault has been diagnosed. No data has been lost, and pages of the affected Memory Module are being retired as errors are encountered. The recommended service action for this event is to schedule replacement of the affected Memory Modules at the earliest possible convenience. Refer to %s for more information." +msgid "INTEL-8001-AQ.response" +msgstr "Pages of memory associated with this memory module are being removed from service as errors are reported." +msgid "INTEL-8001-AQ.impact" +msgstr "Total system memory capacity will be reduced as pages are retired." +msgid "INTEL-8001-AQ.action" +msgstr "Schedule a repair procedure to replace the affected memory module. Use fmdump -v -u to identify the module." diff --git a/usr/src/cmd/fm/dicts/Makefile b/usr/src/cmd/fm/dicts/Makefile index 8a5d4966d7..858f7e2b68 100644 --- a/usr/src/cmd/fm/dicts/Makefile +++ b/usr/src/cmd/fm/dicts/Makefile @@ -38,7 +38,9 @@ common_DCNAMES = \ SCA1000 i386_DCNAMES = \ - AMD + AMD \ + INTEL \ + GMCA sparc_DCNAMES = \ SCF \ diff --git a/usr/src/cmd/fm/eversholt/files/Makefile.com b/usr/src/cmd/fm/eversholt/files/Makefile.com index 92173e8b7d..88ed0680f2 100644 --- a/usr/src/cmd/fm/eversholt/files/Makefile.com +++ b/usr/src/cmd/fm/eversholt/files/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -36,12 +36,16 @@ USR_PLAT_FM_DIR= $(ROOT)/usr/platform/$(EFT_PLAT)/lib/fm USR_PLAT_EFT_DIR= $(USR_PLAT_FM_DIR)/eft USR_PLAT_EFT_FILES= $(EFT_PLAT_FILES:%=$(USR_PLAT_EFT_DIR)/%) +# +# Default target - specify before including Makefile.rootdirs which would +# otherwise provide a default +# +install: all + include $(SRC)/cmd/fm/eversholt/Makefile.rootdirs all:= FILEMODE = 0444 -install: all - all: $(ROOT_EFT_ROOT) $(USR_PLAT_EFT_FILES) $(ROOT_COMMON_EFT_FILES) install_h lint _msg: diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/Makefile b/usr/src/cmd/fm/eversholt/files/i386/i86pc/Makefile index 0f12c27e70..501fe048ad 100644 --- a/usr/src/cmd/fm/eversholt/files/i386/i86pc/Makefile +++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/Makefile @@ -2,9 +2,8 @@ # 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. +# Common Development and Distribution License (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. @@ -20,12 +19,12 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" EFT_PLAT= i86pc -EFT_PLAT_FILES= amd64.eft +EFT_PLAT_FILES= amd64.eft gcpu.eft gcpu_amd.eft intel.eft include ../../Makefile.com diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc b/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc index 4c90be39a2..3fa8843379 100644 --- a/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc +++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.esc @@ -52,7 +52,7 @@ asru chip/memory-controller/chip-select; * diagnosis of associated faults when the libtopo mem scheme rewrites the * asru in "mem" scheme. */ -#define SET_ADDR (setpayloadprop("asru-physaddr", payloadprop("addr"))) +#define SET_ADDR (setpayloadprop("asru-physaddr", payloadprop("IA32_MCi_ADDR"))) #define SET_OFFSET (setpayloadprop("asru-offset", \ payloadprop("resource[0].hc-specific.offset"))) @@ -535,7 +535,7 @@ event ereport.cpu.amd.nb.dramaddr_par@chip/cpu{within(5s)}; event fault.cpu.amd.dramchannel@chip/memory-controller/dram-channel, FITrate=1000, ASRU=dram-channel; -#define GET_CHANNEL ($chan = (payloadprop("bank-status") >> 32 & 0x200) ? \ +#define GET_CHANNEL ($chan = (payloadprop("IA32_MCi_STATUS") >> 32 & 0x200) ? \ 1 : 0) prop fault.cpu.amd.dramchannel@chip/memory-controller/dram-channel[y] (0)-> diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu.esc b/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu.esc new file mode 100644 index 0000000000..cc098771be --- /dev/null +++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu.esc @@ -0,0 +1,290 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * eversholt rules for generic x86 MCA + * + * Most propogations are generated by preprocessor macros. The event + * declarations are deliberately not part of the propogation macros + * so that we know we have full coverage - propogations defined without + * events, or events not used in propogations, will produce compiler + * whinges. + */ + +#pragma dictionary "GMCA" + +fru motherboard/chip; + +asru motherboard/chip/cpu; + +/* + * Ereports for Simple error codes. + */ + +#define SMPL_EVENT(leafclass) \ + event ereport.cpu.generic-x86.leafclass@chip/cpu { within(1s) } + +SMPL_EVENT(unknown); +SMPL_EVENT(unclassified); +SMPL_EVENT(microcode_rom_parity); +SMPL_EVENT(external); +SMPL_EVENT(frc); +SMPL_EVENT(internal_timer); +SMPL_EVENT(internal_unclassified); + +/* + * Propogations for all but "external" and "unknown" simple errors. + * If the error is uncorrected we produce a fault immediately, otherwise + * we diagnose it to an upset and decalre a fault when the SERD engine + * trips. + */ + +/* Simple fault event */ +event fault.cpu.generic-x86.internal@chip/cpu, + ASRU=motherboard/chip/cpu, FRU=motherboard/chip, + FITrate=1000; + +/* Produced when the correctable engine trips */ +event ereport.cpu.generic-x86.simple_trip@chip/cpu { within(1s) }; + +/* Upset to diagnose corrected events to */ +event upset.cpu.generic-x86.simple@chip/cpu + engine=serd.cpu.generic-x86.simple@chip/cpu; + +/* SERD engine for corrected simple errors */ +engine serd.cpu.generic-x86.simple@chip/cpu, + N=3, T=72h, method=persistent, + trip=ereport.cpu.generic-x86.simple_trip@chip/cpu; + +#define STATUS_UC \ + (payloadprop("error_uncorrected") + 0 == 1) + +/* Diagnose corrected events to upsets */ +prop upset.cpu.generic-x86.simple@chip/cpu + { !STATUS_UC } (1)-> + ereport.cpu.generic-x86.microcode_rom_parity@chip/cpu, + ereport.cpu.generic-x86.internal_timer@chip/cpu, + ereport.cpu.generic-x86.unclassified@chip/cpu, + ereport.cpu.generic-x86.internal_unclassified@chip/cpu, + ereport.cpu.generic-x86.frc@chip/cpu; + +/* When the correctable engine trips, diagnose a fault */ +prop fault.cpu.generic-x86.internal@chip/cpu (0)-> + ereport.cpu.generic-x86.simple_trip@chip/cpu; + +/* Diagnose uncorrected events to faults */ +prop fault.cpu.generic-x86.internal@chip/cpu + { STATUS_UC } (0)-> + ereport.cpu.generic-x86.microcode_rom_parity@chip/cpu, + ereport.cpu.generic-x86.internal_timer@chip/cpu, + ereport.cpu.generic-x86.unclassified@chip/cpu, + ereport.cpu.generic-x86.internal_unclassified@chip/cpu, + ereport.cpu.generic-x86.frc@chip/cpu; + +/* + * Ereports for Compound error codes. These are in pairs "foo" and "foo_uc" + * for the corrected and uncorrected version of each error type. All are + * detected at chip/cpu. + */ + +#define CMPND_EVENT(leafclass) \ + event ereport.cpu.generic-x86.leafclass@chip/cpu { within(1s) }; \ + event ereport.cpu.generic-x86.leafclass/**/_uc@chip/cpu { within(1s) } + +/* + * Ereports for Compound error codes - generic memory hierarchy errors + */ +CMPND_EVENT(l0cache); +CMPND_EVENT(l1cache); +CMPND_EVENT(l2cache); +CMPND_EVENT(cache); + +/* + * Ereports for Compound error codes - TLB errors + */ +CMPND_EVENT(l0dtlb); +CMPND_EVENT(l1dtlb); +CMPND_EVENT(l2dtlb); +CMPND_EVENT(dtlb); + +CMPND_EVENT(l0itlb); +CMPND_EVENT(l1itlb); +CMPND_EVENT(l2itlb); +CMPND_EVENT(itlb); + +CMPND_EVENT(l0tlb); +CMPND_EVENT(l1tlb); +CMPND_EVENT(l2tlb); +CMPND_EVENT(tlb); + +/* + * Ereports for Compound error codes - memory hierarchy errors + */ +CMPND_EVENT(l0dcache); +CMPND_EVENT(l1dcache); +CMPND_EVENT(l2dcache); +CMPND_EVENT(dcache); + +CMPND_EVENT(l0icache); +CMPND_EVENT(l1icache); +CMPND_EVENT(l2icache); +CMPND_EVENT(icache); + +/* + * Ereports for Compound error codes - bus and interconnect errors + */ +CMPND_EVENT(bus_interconnect); +CMPND_EVENT(bus_interconnect_memory); +CMPND_EVENT(bus_interconnect_io); + +/* + * Compound error propogations for all but bus_interconnect*. + * + * We resist the temptation propogate, for example, a single dcache fault + * to all ereports mentioning dcache (l0dcache, l1dcache, l2dcache, dcache). + * Instead we will diagnose a distinct fault for each possible cache level, + * whether or not current chips have dcaches at all levels. + * + * Corrected errors are SERDed and produce a fault when the engine fires; + * the same fault is diagnosed immediately for a corresponding uncorrected + * error. + */ + +#define CMPND_FLT_PROP_1(erptleaf, fltleaf, n, t) \ + /* Declare the fault that we can diagnose here */ \ + event fault.cpu.generic-x86.fltleaf@chip/cpu, \ + FITrate=1000, \ + FRU=motherboard/chip, \ + ASRU=motherboard/chip/cpu; \ + \ + /* Produced when the correctable engine trips */ \ + event ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu \ + { within(1s) }; \ + \ + /* Upset to diagnose corrected events to */ \ + event upset.cpu.generic-x86.fltleaf@chip/cpu, \ + engine=serd.cpu.generic-x86.fltleaf@chip/cpu; \ + \ + /* SERD engine for corrected events */ \ + engine serd.cpu.generic-x86.fltleaf@chip/cpu, \ + N=n, T=t, method=persistent, \ + trip=ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu; \ + \ + /* Diagnose corrected events to the corresponding upset */ \ + prop upset.cpu.generic-x86.fltleaf@chip/cpu (1)-> \ + ereport.cpu.generic-x86.erptleaf@chip/cpu; \ + \ + /* When the engine trips, diagnose a fault */ \ + prop fault.cpu.generic-x86.fltleaf@chip/cpu (0)-> \ + ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu; \ + \ + /* Produce immediate faults for uncorrected errors */ \ + prop fault.cpu.generic-x86.fltleaf@chip/cpu (0)-> \ + ereport.cpu.generic-x86.erptleaf/**/_uc@chip/cpu + + +CMPND_FLT_PROP_1(l0cache, l0cache, 3, 72h); +CMPND_FLT_PROP_1(l1cache, l1cache, 3, 72h); +CMPND_FLT_PROP_1(l2cache, l2cache, 3, 72h); +CMPND_FLT_PROP_1(cache, cache, 12, 72h); + +CMPND_FLT_PROP_1(l0dtlb, l0dtlb, 3, 72h); +CMPND_FLT_PROP_1(l1dtlb, l1dtlb, 3, 72h); +CMPND_FLT_PROP_1(l2dtlb, l2dtlb, 3, 72h); +CMPND_FLT_PROP_1(dtlb, dtlb, 12, 72h); + +CMPND_FLT_PROP_1(l0itlb, l0itlb, 3, 72h); +CMPND_FLT_PROP_1(l1itlb, l1itlb, 3, 72h); +CMPND_FLT_PROP_1(l2itlb, l2itlb, 3, 72h); +CMPND_FLT_PROP_1(itlb, itlb, 12, 72h); + +CMPND_FLT_PROP_1(l0tlb, l0tlb, 3, 72h); +CMPND_FLT_PROP_1(l1tlb, l1tlb, 3, 72h); +CMPND_FLT_PROP_1(l2tlb, l2tlb, 3, 72h); +CMPND_FLT_PROP_1(tlb, tlb, 12, 72h); + +CMPND_FLT_PROP_1(l0dcache, l0dcache, 3, 72h); +CMPND_FLT_PROP_1(l1dcache, l1dcache, 3, 72h); +CMPND_FLT_PROP_1(l2dcache, l2dcache, 3, 72h); +CMPND_FLT_PROP_1(dcache, dcache, 12, 72h); + +CMPND_FLT_PROP_1(l0icache, l0icache, 3, 72h); +CMPND_FLT_PROP_1(l1icache, l1icache, 3, 72h); +CMPND_FLT_PROP_1(l2icache, l2icache, 3, 72h); +CMPND_FLT_PROP_1(icache, icache, 12, 72h); + +/* + * Compound error propogations for bus_interconnect* - as above but + * with no FRU. + */ + +#define CMPND_FLT_PROP_2(erptleaf, fltleaf, n, t) \ + /* Declare the fault that we can diagnose here */ \ + event fault.cpu.generic-x86.fltleaf@chip/cpu, \ + FITrate=1000, \ + ASRU=motherboard/chip/cpu; \ + \ + /* Produced when the correctable engine trips */ \ + event ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu \ + { within(1s) }; \ + \ + /* Upset to diagnose corrected events to */ \ + event upset.cpu.generic-x86.fltleaf@chip/cpu, \ + engine=serd.cpu.generic-x86.fltleaf@chip/cpu; \ + \ + /* SERD engine for corrected events */ \ + engine serd.cpu.generic-x86.fltleaf@chip/cpu, \ + N=n, T=t, method=persistent, \ + trip=ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu; \ + \ + /* Diagnose corrected events to the corresponding upset */ \ + prop upset.cpu.generic-x86.fltleaf@chip/cpu (1)-> \ + ereport.cpu.generic-x86.erptleaf@chip/cpu; \ + \ + /* When the engine trips, diagnose a fault */ \ + prop fault.cpu.generic-x86.fltleaf@chip/cpu (0)-> \ + ereport.cpu.generic-x86.fltleaf/**/_error@chip/cpu; \ + \ + /* Produce immediate faults for uncorrected errors */ \ + prop fault.cpu.generic-x86.fltleaf@chip/cpu (0)-> \ + ereport.cpu.generic-x86.erptleaf/**/_uc@chip/cpu + + +CMPND_FLT_PROP_2(bus_interconnect, bus_interconnect, 10, 72h); +CMPND_FLT_PROP_2(bus_interconnect_memory, bus_interconnect_memory, 10, 72h); +CMPND_FLT_PROP_2(bus_interconnect_io, bus_interconnect_io, 10, 72h); + +/* + * Discards - not enough info to diagnose. + */ +event upset.discard@chip/cpu; + +prop upset.discard@chip/cpu (0)-> + ereport.cpu.generic-x86.external@chip/cpu, + ereport.cpu.generic-x86.unknown@chip/cpu; + diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu_amd.esc b/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu_amd.esc new file mode 100644 index 0000000000..f6c81d74f3 --- /dev/null +++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu_amd.esc @@ -0,0 +1,347 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Eversholt rules for generic AMD with on-chip memory-controller(s), as seen + * in AMD family 0xf and 0x10. + * + * In the absence of any model-specific support, any memory errors that + * are observed via MCA (typically through an on-chip memory-controller) + * will surface as ereport.cpu.generic-x86.bus_interconnect_memory[_uc] + * ereports and are diagnosed via generic rules in gcpu.esc. + * + * If full model-specific support is available, including full NorthBridge + * support, then memory ereports will surface in a more-specific subclass + * such as ereport.cpu.amd.mem_ce; these are diagnosed in amd64.esc. + * + * In the case where some "vendor generic" support is present, memory errors + * are reported as ereport.cpu.generic-x86.mem_{ce,ue} and include a + * syndrome and syndrome-type, and usually also a resource FMRI to identify + * the affected resource. In the AMD case a resource FMRI is included for + * those chip versions that include an Online Spare Control register; this + * register provides counts of ECC errors seen per channel and chip-select + * on a NorthBridge node. The resource FMRI has form + * hc:///motherboard/chip/memory-controller/dram-channel/chip-select + * in these cases. + */ + +#pragma dictionary "GMCA" + +/* + * The number of pages that must be faulted on a chip-select for repeated + * correctable errors before we will consider one of the component dimms + * faulty. + */ +#define CS_DIMMSB_THRESH 64 + +/* + * The maximum number of pages we will diagnose as faulty on any one + * chip-select (must be at least CS_PAGEFLT_THRESH). If a chip-select + * has a fault that will affect zillions of pages this limit stops us + * diagnosing excessive numbers of page faults. + */ +#define CS_PAGEFLT_MAX (2 * CS_DIMMSB_THRESH) + +/* + * SERD paramters for individual page faults. When more than PAGE_SB_COUNT + * correctable ereports are experienced on a single chip-select within + * PAGE_SB_TIME the engine will fire and we will fault the most recent + * page. + */ +#define PAGE_SB_COUNT 3 +#define PAGE_SB_TIME 24h + +fru chip; + +#define CSPATH chip/memory-controller/dram-channel/chip-select + +asru chip/cpu; +asru CSPATH; + +/* + * ADDR_VALID is true if the ereport payload includes IA32_MCi_ADDR. + */ +#define ADDR_VALID (payloadprop_defined("IA32_MCi_ADDR")) + +/* + * CONTAINS_CS is true if the resource nvlist array exists and one of its + * members matches the chip-select path. This is used to constrain + * propogations to those for which a resource element matches the + * chip-select path of the propogation. This is necessary because the + * detector element of memory ereports is a cpu and not the chip-select itself. + */ +#define CONTAINS_CS (payloadprop_contains("resource", asru(CSPATH))) + +#define SET_ADDR (setpayloadprop("asru-physaddr", payloadprop("IA32_MCi_ADDR"))) +/* Generic memory ereports. */ +event ereport.cpu.generic-x86.mem_ce@chip/cpu { within(1s) }; +event ereport.cpu.generic-x86.mem_ue@chip/cpu { within(1s) }; + +/* + * ========= Propogations for correctable page faults ============ + * | | + * | Discard mem_ce with no resource in the ereport payload. | + * | Discard mem_ce with no address info - we can't fault the | + * | corresponding page without it. | + * | | + * | For a mem_ce ereport detected by a given chip/cpu (as per | + * | the payload detector info) whose resource payload member | + * | includes a chip/memory-controller/dram-channel/chip-select | + * | (CSPATH) for the same chip number, diagnose to an upset event | + * | associated with a per-CSPATH SERD engine as long as we are | + * | below the page fault limit for this CSPATH (defined below); | + * | if we are over that limit then discard the event since we | + * | will already have faulted a dimm and there is no point in | + * | continuing to diagnose endless page faults from a dimm with | + * | something like a pin failure. | + * | | + * | When the per-CSPATH SERD engine fires we fault the page | + * | containing the address included in the ereport that caused | + * | the trip, and increment a per-CSPATH counter to count page | + * | faults on that chip-select from repeated correctable errors. | + * | | + * | A mem_ue ereport produces an immediate page_ue fault. | + * |===============================================================| + */ + +/* Counter for page faults diagnosed on a chip-select */ +engine stat.cepgflt@CSPATH; + +#define CS_PGFLT_LIMIT_REACHED (count(stat.cepgflt@CSPATH) > CS_PAGEFLT_MAX) + +/* Page fault event for repeated correctable errors */ +event fault.memory.generic-x86.page_ce@CSPATH, + FITrate=1000, /* meaningless */ + message=0, /* do not message individual pageflts */ + ASRU=CSPATH, + count=stat.cepgflt@CSPATH, /* increment on pageflt diagnosis */ + action=confcall("rewrite-ASRU"); /* identify page in chip-select */ + +/* Upset to diagnose correctable ereports to */ +event upset.memory.generic-x86.page_ce@CSPATH, + engine=serd.memory.generic-x86.page_ce@CSPATH; + +/* Synthetic ereport generated when page_ce SERD engine trips */ +event ereport.memory.generic-x86.page_ce_trip@CSPATH { within(1s) }; + +/* SERD engine for each chip-select */ +engine serd.memory.generic-x86.page_ce@CSPATH, + N=PAGE_SB_COUNT, T=PAGE_SB_TIME, + method=persistent, + trip=ereport.memory.generic-x86.page_ce_trip@CSPATH; + +/* Upset to discard events to when we're over limit */ +event upset.memory.generic-x86.overpgfltlimit@CSPATH; + +/* + * Discard ereports with no resource or no address info + */ +event upset.memory.generic-x86.discard@chip/cpu; +prop upset.memory.generic-x86.discard@chip/cpu + { !payloadprop_defined("resource") || !ADDR_VALID } (1)-> + ereport.cpu.generic-x86.mem_ce@chip/cpu; + +/* + * For as long as we are below the page fault limit diagnose correctable ereport + * observations as upsets to feed the SERD engine. + */ +prop upset.memory.generic-x86.page_ce@CSPATH + { ADDR_VALID && CONTAINS_CS && !CS_PGFLT_LIMIT_REACHED } (0)-> + ereport.cpu.generic-x86.mem_ce@chip/cpu; + +/* + * Discard ereports if we are above the page fault limit on this chip-select, + */ +prop upset.memory.generic-x86.overpgfltlimit@CSPATH + { ADDR_VALID && CONTAINS_CS && CS_PGFLT_LIMIT_REACHED } (1)-> + ereport.cpu.generic-x86.mem_ce@chip/cpu; + +/* Diagnose a page fault when the pagefault SERD engine trips */ +prop fault.memory.generic-x86.page_ce@CSPATH (1)-> + ereport.memory.generic-x86.page_ce_trip@CSPATH; + +/* Include address info in the page fault diagnosed, for rewrite-ASRU */ +prop fault.memory.generic-x86.page_ce@CSPATH + { ADDR_VALID && CONTAINS_CS && SET_ADDR } (0)-> + ereport.cpu.generic-x86.mem_ce@chip/cpu; + +/* + * ========= Propogations for correctable DIMM faults ============ + * | | + * | A dimm_ce fault is diagnosed when we have faulted an | + * | excessive number of page_ce faults on a chip-select - more | + * | than CE_DIMMSB_THRESH. | + * | | + * | A dimm_ue fault is diagnosed on the first uncorrectable | + * | ereport from a chip-select. | + * |===============================================================| + */ + +/* DIMM fault event for CE failures */ +event fault.memory.generic-x86.dimm_ce@CSPATH, + ASRU=CSPATH, + FITrate=1000, /* meaningless */ + action=confcall("rewrite-ASRU"); /* rewrite in "mem" FMRI scheme */ + +#define CS_DIMMSB_THRESH_REACHED \ + (count(stat.cepgflt@CSPATH) == CS_DIMMSB_THRESH) + +/* + * This upset is diagnosed in parallel with upset.memory.generic-x86.page_ce + * on the CSPATH, and the associated SERD engine has the same parameters + * as serd.memory.generic-x86.page_ce@CSPATH so they fire at the same time. + * When this one fires we check whether we have reached the diagnosis + * threshold for a dimm_ce. + */ +event upset.memory.generic-x86.dimm_ce@CSPATH, + engine=serd.memory.generic-x86.dimm_ce_limitchk@CSPATH; + +event ereport.memory.generic-x86.dimm_ce_limitchk@CSPATH { within(1s) }; + +engine serd.memory.generic-x86.dimm_ce_limitchk@CSPATH, + N=PAGE_SB_COUNT, T=PAGE_SB_TIME, + method=persistent, + trip=ereport.memory.generic-x86.dimm_ce_limitchk@CSPATH; + +prop upset.memory.generic-x86.dimm_ce@CSPATH + { ADDR_VALID && CONTAINS_CS } (0)-> + ereport.cpu.generic-x86.mem_ce@chip/cpu; + +prop fault.memory.generic-x86.dimm_ce@CSPATH + { CS_DIMMSB_THRESH_REACHED } (0)-> + ereport.memory.generic-x86.dimm_ce_limitchk@CSPATH; + +event upset.memory.generic-x86.discard2@CSPATH; +prop upset.memory.generic-x86.discard2@CSPATH + { !CS_DIMMSB_THRESH_REACHED } (0)-> + ereport.memory.generic-x86.dimm_ce_limitchk@CSPATH; + +/* + * ========= Propogations for uncorrectable page faults ========== + * | | + * | A UE produces an immediate page fault. But we also want a | + * | corresponding dimm fault and since we do not like multi-entry | + * | suspect lists we arrange two distinct fault management | + * | exercises by diagnosing a mem_ue to two upset events that | + * | feed instant-trip SERD engines. Yuck. | + * |===============================================================| + */ + +/* Page fault event for uncorrectable errors */ +event fault.memory.generic-x86.page_ue@CSPATH, + FITrate=1000, /* meaningless */ + message=0, /* do not message individual pageflts */ + count=stat.cepgflt@CSPATH, /* increment on pageflt diagnosis */ + action=confcall("rewrite-ASRU"); /* identify page in chip-select */ + +/* Upset for page fault */ +event upset.memory.generic-x86.page_ue@CSPATH, + engine=serd.memory.generic-x86.page_ue@CSPATH; + +/* Synthetic erport generated when the page_ue SERD engine trips */ +event ereport.memory.generic-x86.page_ue_trip@CSPATH { within(1s) }; + +/* Instant-trip engine for page fault */ +engine serd.memory.generic-x86.page_ue@CSPATH, + N=0, T=1h, /* trip on first upset */ + method=persistent, + trip=ereport.memory.generic-x86.page_ue_trip@CSPATH; + +/* Discard events with no address info */ +event upset.memory.generic-x86.discard3@CSPATH; +prop upset.memory.generic-x86.discard3@CSPATH + { !payloadprop_defined("resource") || !ADDR_VALID } (1)-> + ereport.cpu.generic-x86.mem_ue@chip/cpu; + +/* Diagnose a page_ue upset on a mem_ue event */ +prop upset.memory.generic-x86.page_ue@CSPATH + { ADDR_VALID && CONTAINS_CS } (0)-> + ereport.cpu.generic-x86.mem_ue@chip/cpu; + +/* On the immediate SERD trip diagnose a page fault */ +prop fault.memory.generic-x86.page_ue@CSPATH (1)-> + ereport.memory.generic-x86.page_ue_trip@CSPATH; + +/* Include address info in the page fault diagnosed, for rewrite-ASRU */ +prop fault.memory.generic-x86.page_ue@CSPATH + { ADDR_VALID && CONTAINS_CS && SET_ADDR } (0)-> + ereport.cpu.generic-x86.mem_ue@chip/cpu; + +/* + * ========= Propogations for uncorrectable dimm faults ========== + * | | + * | A UE produces an immediate dimm fault. As explained in the | + * | page_ue block comment above we split the exercise in two in | + * | order to produce independent page_ue and dimm_ue diagnoses. | + * |===============================================================| + */ + +/* Dimm fault for an uncorrectable error */ +event fault.memory.generic-x86.dimm_ue@CSPATH, + ASRU=CSPATH, + FITrate=1000, /* meaningless */ + action=confcall("rewrite-ASRU"); /* rewrite in "mem" FMRI scheme */ + +/* Upset for dimm fault */ +event upset.memory.generic-x86.dimm_ue@CSPATH, + engine=serd.memory.generic-x86.dimm_ue@CSPATH; + +/* Sythetic ereport generated when the dimm_ue SERD engine trips */ +event ereport.memory.generic-x86.dimm_ue_trip@CSPATH { within(1s) }; + +/* Instant-trip engine for dimm fault */ +engine serd.memory.generic-x86.dimm_ue@CSPATH, + N=0, T=1h, /* trip on first upset */ + method=persistent, + trip=ereport.memory.generic-x86.dimm_ue_trip@CSPATH; + +/* Diagnose a dimm_ue upset on a mem_ue event (in addition to page_ue upset) */ +prop upset.memory.generic-x86.dimm_ue@CSPATH + { CONTAINS_CS } (0)-> + ereport.cpu.generic-x86.mem_ue@chip/cpu; + +/* On the immediate SERD trip diagnose a dimm fault */ +prop fault.memory.generic-x86.dimm_ue@CSPATH (1)-> + ereport.memory.generic-x86.dimm_ue_trip@CSPATH; + +/* + * ========= Propogations for GART Table Walk Errors ============= + * | | + * | These are usually due to software mis-programming of the GART | + * | TLB rather than from hardware errors. It would be incorrect | + * | to fault and potentially offline a cpu in response to these | + * | so they have their own fault class to facilitate us ignoring | + * | them. | + * |===============================================================| + */ + +event ereport.cpu.generic-x86.gart_tbl_walk@chip/cpu { within(1s) }; +event upset.cpu.generic-x86.gart_tbl_walk@chip/cpu; + +prop upset.cpu.generic-x86.gart_tbl_walk@chip/cpu (1)-> + ereport.cpu.generic-x86.gart_tbl_walk@chip/cpu; diff --git a/usr/src/cmd/fm/eversholt/files/i386/i86pc/intel.esc b/usr/src/cmd/fm/eversholt/files/i386/i86pc/intel.esc new file mode 100644 index 0000000000..f2dc3de18d --- /dev/null +++ b/usr/src/cmd/fm/eversholt/files/i386/i86pc/intel.esc @@ -0,0 +1,732 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#pragma dictionary "INTEL" + +/* + * Eversholt rules for the intel CPU/Memory + */ + +/* CPU errors detected through MCA */ + +fru motherboard/chip; + +asru motherboard/chip/cpu; + +/* + * Ereports for Simple error codes. + */ + +#define SMPL_EVENT(leafclass, t) \ + event ereport.cpu.intel.leafclass@chip/cpu { within(t) } + +SMPL_EVENT(unknown, 1s); +SMPL_EVENT(unclassified, 1s); +SMPL_EVENT(microcode_rom_parity, 1s); +SMPL_EVENT(external, 1s); +SMPL_EVENT(frc, 1s); +SMPL_EVENT(internal_timer, 1s); +SMPL_EVENT(internal_unclassified, 1s); + +/* + * Propogations for all but "external" and "unknown" simple errors. + * If the error is uncorrected we produce a fault immediately, otherwise + * we diagnose it to an upset and decalre a fault when the SERD engine + * trips. + */ + +/* Simple fault event */ +event fault.cpu.intel.internal@chip/cpu, + ASRU=motherboard/chip/cpu, FRU=motherboard/chip, + FITrate=1000; + +/* Produced when the correctable engine trips */ +event ereport.cpu.intel.simple_trip@chip/cpu { within(1s) }; + +/* Upset to diagnose corrected events to */ +event upset.cpu.intel.simple@chip/cpu + engine=serd.cpu.intel.simple@chip/cpu; + +/* SERD engine for corrected simple errors */ +engine serd.cpu.intel.simple@chip/cpu, + N=3, T=72h, method=persistent, + trip=ereport.cpu.intel.simple_trip@chip/cpu; + +#define STATUS_UC \ + (payloadprop("error_uncorrected") + 0 == 1) + +/* Diagnose corrected events to upsets */ +prop upset.cpu.intel.simple@chip/cpu + { !STATUS_UC } (1)-> + ereport.cpu.intel.microcode_rom_parity@chip/cpu, + ereport.cpu.intel.internal_timer@chip/cpu, + ereport.cpu.intel.unclassified@chip/cpu, + ereport.cpu.intel.internal_unclassified@chip/cpu, + ereport.cpu.intel.frc@chip/cpu; + +/* When the correctable engine trips, diagnose a fault */ +prop fault.cpu.intel.internal@chip/cpu (0)-> + ereport.cpu.intel.simple_trip@chip/cpu; + +/* Diagnose uncorrected events to faults */ +prop fault.cpu.intel.internal@chip/cpu + { STATUS_UC } (0)-> + ereport.cpu.intel.microcode_rom_parity@chip/cpu, + ereport.cpu.intel.internal_timer@chip/cpu, + ereport.cpu.intel.unclassified@chip/cpu, + ereport.cpu.intel.internal_unclassified@chip/cpu, + ereport.cpu.intel.frc@chip/cpu; + +/* + * Ereports for Compound error codes. These are in pairs "foo" and "foo_uc" + * for the corrected and uncorrected version of each error type. All are + * detected at chip/cpu. + */ + +#define CMPND_EVENT(leafclass, t) \ + event ereport.cpu.intel.leafclass@chip/cpu { within(t) }; \ + event ereport.cpu.intel.leafclass/**/_uc@chip/cpu { within(t) } + +/* + * Ereports for Compound error codes - intel errors + */ +CMPND_EVENT(l0cache, 1s); +CMPND_EVENT(l1cache, 1s); +CMPND_EVENT(l2cache, 1s); +CMPND_EVENT(cache, 1s); + +/* + * Ereports for Compound error codes - TLB errors + */ +CMPND_EVENT(l0dtlb, 1s); +CMPND_EVENT(l1dtlb, 1s); +CMPND_EVENT(l2dtlb, 1s); +CMPND_EVENT(dtlb, 1s); + +CMPND_EVENT(l0itlb, 1s); +CMPND_EVENT(l1itlb, 1s); +CMPND_EVENT(l2itlb, 1s); +CMPND_EVENT(itlb, 1s); + +CMPND_EVENT(l0tlb, 1s); +CMPND_EVENT(l1tlb, 1s); +CMPND_EVENT(l2tlb, 1s); +CMPND_EVENT(tlb, 1s); + +/* + * Ereports for Compound error codes - memory hierarchy errors + */ +CMPND_EVENT(l0dcache, 1s); +CMPND_EVENT(l1dcache, 1s); +CMPND_EVENT(l2dcache, 1s); +CMPND_EVENT(dcache, 1s); + +CMPND_EVENT(l0icache, 1s); +CMPND_EVENT(l1icache, 1s); +CMPND_EVENT(l2icache, 1s); +CMPND_EVENT(icache, 1s); + +/* + * Ereports for Compound error codes - bus and interconnect errors + */ +CMPND_EVENT(bus_interconnect, 1s); +CMPND_EVENT(bus_interconnect_memory, 1s); +CMPND_EVENT(bus_interconnect_io, 1s); + +/* + * Compound error propogations. + * + * We resist the temptation propogate, for example, a single dcache fault + * to all ereports mentioning dcache (l0dcache, l1dcache, l2dcache, dcache). + * Instead we will diagnose a distinct fault for each possible cache level, + * whether or not current chips have dcaches at all levels. + * + * Corrected errors are SERDed and produce a fault when the engine fires; + * the same fault is diagnosed immediately for a corresponding uncorrected + * error. + */ + +#define CMPND_FLT_PROP_1(erptleaf, fltleaf, n, t) \ + /* Declare the fault that we can diagnose here */ \ + event fault.cpu.intel.fltleaf@chip/cpu, \ + FITrate=1000, \ + FRU=motherboard/chip, \ + ASRU=motherboard/chip/cpu; \ + \ + /* Produced when the correctable engine trips */ \ + event ereport.cpu.intel.fltleaf/**/_error@chip/cpu { within(1s) }; \ + \ + /* Upset to diagnose corrected events to */ \ + event upset.cpu.intel.fltleaf@chip/cpu, \ + engine=serd.cpu.intel.fltleaf@chip/cpu; \ + \ + /* SERD engine for corrected events */ \ + engine serd.cpu.intel.fltleaf@chip/cpu, \ + N=n, T=t, method=persistent, \ + trip=ereport.cpu.intel.fltleaf/**/_error@chip/cpu; \ + \ + /* Diagnose corrected events to the corresponding upset */ \ + prop upset.cpu.intel.fltleaf@chip/cpu (1)-> \ + ereport.cpu.intel.erptleaf@chip/cpu; \ + \ + /* When the engine trip, diagnose a fault */ \ + prop fault.cpu.intel.fltleaf@chip/cpu (0)-> \ + ereport.cpu.intel.fltleaf/**/_error@chip/cpu; \ + \ + /* Produce immediate faults for uncorrected errors */ \ + prop fault.cpu.intel.fltleaf@chip/cpu (0)-> \ + ereport.cpu.intel.erptleaf/**/_uc@chip/cpu + +#define CMPND_FLT_PROP_2(erptleaf, fltleaf, n, t) \ + /* Declare the fault that we can diagnose here */ \ + event fault.cpu.intel.fltleaf@chip/cpu, \ + FITrate=1, \ + ASRU=motherboard/chip/cpu; \ + \ + /* Produced when the correctable engine trips */ \ + event ereport.cpu.intel.fltleaf/**/_error@chip/cpu { within(1s) }; \ + \ + /* Upset to diagnose corrected events to */ \ + event upset.cpu.intel.fltleaf@chip/cpu, \ + engine=serd.cpu.intel.fltleaf@chip/cpu; \ + \ + /* SERD engine for corrected events */ \ + engine serd.cpu.intel.fltleaf@chip/cpu, \ + N=n, T=t, method=persistent, \ + trip=ereport.cpu.intel.fltleaf/**/_error@chip/cpu; \ + \ + /* Diagnose corrected events to the corresponding upset */ \ + prop upset.cpu.intel.fltleaf@chip/cpu (1)-> \ + ereport.cpu.intel.erptleaf@chip/cpu; \ + \ + /* When the engine trip, diagnose a fault */ \ + prop fault.cpu.intel.fltleaf@chip/cpu (0)-> \ + ereport.cpu.intel.fltleaf/**/_error@chip/cpu; \ + \ + /* Produce immediate faults for uncorrected errors */ \ + prop fault.cpu.intel.fltleaf@chip/cpu (0)-> \ + ereport.cpu.intel.erptleaf/**/_uc@chip/cpu + + +CMPND_FLT_PROP_1(l0cache, l0cache, 3, 72h); +CMPND_FLT_PROP_1(l1cache, l1cache, 3, 72h); +CMPND_FLT_PROP_1(l2cache, l2cache, 3, 72h); +CMPND_FLT_PROP_1(cache, cache, 12, 72h); + +CMPND_FLT_PROP_1(l0dtlb, l0dtlb, 3, 72h); +CMPND_FLT_PROP_1(l1dtlb, l1dtlb, 3, 72h); +CMPND_FLT_PROP_1(l2dtlb, l2dtlb, 3, 72h); +CMPND_FLT_PROP_1(dtlb, dtlb, 12, 72h); + +CMPND_FLT_PROP_1(l0itlb, l0itlb, 3, 72h); +CMPND_FLT_PROP_1(l1itlb, l1itlb, 3, 72h); +CMPND_FLT_PROP_1(l2itlb, l2itlb, 3, 72h); +CMPND_FLT_PROP_1(itlb, itlb, 12, 72h); + +CMPND_FLT_PROP_1(l0tlb, litlb, 3, 72h); +CMPND_FLT_PROP_1(l1tlb, litlb, 3, 72h); +CMPND_FLT_PROP_1(l2tlb, litlb, 3, 72h); +CMPND_FLT_PROP_1(tlb, tlb, 12, 72h); + +CMPND_FLT_PROP_1(l0dcache, l0dcache, 3, 72h); +CMPND_FLT_PROP_1(l1dcache, l1dcache, 3, 72h); +CMPND_FLT_PROP_1(l2dcache, l2dcache, 3, 72h); +CMPND_FLT_PROP_1(dcache, dcache, 12, 72h); + +CMPND_FLT_PROP_1(l0icache, l0icache, 3, 72h); +CMPND_FLT_PROP_1(l1icache, l1icache, 3, 72h); +CMPND_FLT_PROP_1(l2icache, l2icache, 3, 72h); +CMPND_FLT_PROP_1(icache, icache, 12, 72h); + +CMPND_FLT_PROP_2(bus_interconnect, bus_interconnect, 10, 72h); +CMPND_FLT_PROP_2(bus_interconnect_memory, bus_interconnect_memory, 10, 72h); +CMPND_FLT_PROP_2(bus_interconnect_io, bus_interconnect_io, 10, 72h); + +event upset.discard@chip/cpu; + +prop upset.discard@chip/cpu (0)-> + ereport.cpu.intel.external@chip/cpu, + ereport.cpu.intel.unknown@chip/cpu; + +/* errors detected in northbridge */ + + +/* + * SET_ADDR and SET_OFFSET are used to set a payload value in the fault that + * we diagnose for page faults, to record the physical address of the faulting + * page. The "asru-" prefix is hooked in the "rewrite-ASRU" confcalls made on + * diagnosis of associated faults when the libtopo mem scheme rewrites the + * asru in "mem" scheme. + */ +#define SET_ADDR (!payloadprop_defined("physaddr") || \ + setpayloadprop("asru-physaddr", payloadprop("physaddr"))) + +#define SET_OFFSET (!payloadprop_defined("offset") || \ + setpayloadprop("asru-offset", payloadprop("offset"))) + +#define CE_PGFLTS \ + (count(stat.ce_pgflt@motherboard/memory-controller/dram-channel/dimm)) +#define UE_DIMM \ + (count(stat.ue_dimm@motherboard/memory-controller/dram-channel/dimm)) + +#define PAGE_FIT 1 +#define PAGE_CE_COUNT 2 +#define PAGE_CE_TIME 72h + +fru motherboard; +asru motherboard; +fru motherboard/memory-controller/dram-channel; +asru motherboard/memory-controller/dram-channel; +fru motherboard/memory-controller/dram-channel/dimm; +asru motherboard/memory-controller/dram-channel/dimm; +asru motherboard/memory-controller/dram-channel/dimm/rank; +asru motherboard/chip; + +engine stat.ce_pgflt@motherboard/memory-controller/dram-channel/dimm; +engine stat.ue_dimm@motherboard/memory-controller/dram-channel/dimm; + +event ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller{within(12s)}; +event ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller{within(12s)}; +event fault.memory.intel.page_ue@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=PAGE_FIT, + ASRU=motherboard/memory-controller/dram-channel/dimm/rank, message=0, + action=confcall("rewrite-ASRU"); /* rewrite ASRU to identify page in rank */ +event ereport.memory.page_ue_trip@motherboard/memory-controller{within(12s)}; +engine serd.memory.intel.page_ue@motherboard/memory-controller, + N=0, T=1h, method=persistent, + trip=ereport.memory.page_ue_trip@motherboard/memory-controller; +event upset.memory.intel.page_ue@motherboard/memory-controller, + engine=serd.memory.intel.page_ue@motherboard/memory-controller; + +prop upset.memory.intel.page_ue@motherboard/memory-controller (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +prop upset.memory.intel.page_ue@motherboard/memory-controller (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop fault.memory.intel.page_ue@ + motherboard/memory-controller/dram-channel/dimm/rank[rank_num] + { UE_DIMM > 0 && payloadprop_defined("rank") && + rank_num == payloadprop("rank") && + (payloadprop_defined("physaddr") || payloadprop_defined("offset")) && + SET_ADDR && SET_OFFSET } (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop fault.memory.intel.page_ue@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.memory.page_ue_trip@motherboard/memory-controller; + +event upset.memory.intel.discard@motherboard/memory-controller{within(1s)}; + +prop upset.memory.intel.discard@motherboard/memory-controller + { !payloadprop_defined("rank") || (!payloadprop_defined("physaddr") && + !payloadprop_defined("offset")) } (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop upset.memory.intel.discard@motherboard/memory-controller (0)-> + ereport.memory.page_ue_trip@motherboard/memory-controller, + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +#define DIMM_UE_FIT 1000 + +event fault.memory.intel.dimm_ue@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=DIMM_UE_FIT, FRU=motherboard/memory-controller/dram-channel/dimm, + ASRU=motherboard/memory-controller/dram-channel/dimm/rank, + count=stat.dimm_flt@motherboard/memory-controller/dram-channel/dimm, + action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */ +event ereport.memory.dimm_ue_trip@motherboard/memory-controller{within(12s)}; +engine serd.memory.intel.dimm_ue@motherboard/memory-controller, + N=0, T=1h, method=persistent, + trip=ereport.memory.dimm_ue_trip@motherboard/memory-controller; +event upset.memory.intel.dimm_ue@motherboard/memory-controller, + engine=serd.memory.intel.dimm_ue@motherboard/memory-controller; + +prop upset.memory.intel.dimm_ue@ motherboard/memory-controller (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop fault.memory.intel.dimm_ue@ + motherboard/memory-controller/dram-channel<channel_num>/dimm/rank[rank_num] + { payloadprop_defined("rank") && rank_num == payloadprop("rank") } (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop upset.memory.intel.dimm_ue@ + motherboard/memory-controller (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +prop fault.memory.intel.dimm_ue@ + motherboard/memory-controller/dram-channel/dimm/rank (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +prop fault.memory.intel.dimm_ue@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.memory.dimm_ue_trip@motherboard/memory-controller; + +event upset.memory.intel.discard1@motherboard/memory-controller{within(1s)}; + +prop upset.memory.intel.discard1@motherboard/memory-controller + { !payloadprop_defined("rank") } (1)-> + ereport.cpu.intel.nb.mem_ue@motherboard/memory-controller, + ereport.cpu.intel.nb.fbd.ma@motherboard/memory-controller; + +prop upset.memory.intel.discard1@motherboard/memory-controller (0)-> + ereport.memory.dimm_ue_trip@motherboard/memory-controller, + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.memory.intel.page_trip@ + motherboard/memory-controller/dram-channel/dimm/rank{within(12s)}; +event ereport.cpu.intel.nb.mem_ce@ + motherboard/memory-controller/dram-channel/dimm/rank{within(12s)}; + +engine serd.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + N=PAGE_CE_COUNT, T=PAGE_CE_TIME, method=persistent, + trip=ereport.memory.intel.page_trip@ + motherboard/memory-controller/dram-channel/dimm/rank; +event upset.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + engine=serd.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank; + +event fault.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=PAGE_FIT, + ASRU=motherboard/memory-controller/dram-channel/dimm/rank, message=0, + count=stat.ce_pgflt@motherboard/memory-controller/dram-channel/dimm, + action=confcall("rewrite-ASRU"); /* rewrite ASRU to identify page in rank */ + +prop fault.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.memory.intel.page_trip@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop fault.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank + { (payloadprop_defined("physaddr") || payloadprop_defined("offset")) && + SET_ADDR && SET_OFFSET } (0)-> + ereport.cpu.intel.nb.mem_ce@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop upset.memory.intel.page_ce@ + motherboard/memory-controller/dram-channel/dimm/rank + { (payloadprop_defined("physaddr") || payloadprop_defined("offset")) && + SET_ADDR && SET_OFFSET } (1)-> + ereport.cpu.intel.nb.mem_ce@ + motherboard/memory-controller/dram-channel/dimm/rank; + +#define DIMM_CE_FIT 2000 +#define DIMM_CE_COUNT 10 +#define DIMM_CE_TIME 1week + +event fault.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=DIMM_CE_FIT, FRU=motherboard/memory-controller/dram-channel/dimm, + ASRU=motherboard/memory-controller/dram-channel/dimm/rank, + action=confcall("rewrite-ASRU"); /* rewrite non-leaf ASRU in mem scheme */ +event upset.memory.discard@motherboard/memory-controller/dram-channel/dimm/rank; + +event ereport.memory.intel.dimm_trip@ + motherboard/memory-controller/dram-channel/dimm/rank{within(1s)}; +#define DIMM_CE(label, dimm_size, n, t, fault_rate) \ + engine serd.memory.intel.dimm_ce.label/**/@ \ + motherboard/memory-controller/dram-channel/dimm/rank, \ + N=n, T=t, method=persistent, \ + trip=ereport.memory.intel.dimm_trip@ \ + motherboard/memory-controller/dram-channel/dimm/rank; \ + event upset.memory.intel.dimm_ce.label/**/@ \ + motherboard/memory-controller/dram-channel/dimm/rank, \ + engine=serd.memory.intel.dimm_ce.label/**/@ \ + motherboard/memory-controller/dram-channel/dimm/rank; \ + prop upset.memory.intel.dimm_ce.label/**/@ \ + motherboard/memory-controller/dram-channel/dimm/rank \ + {confprop_defined( \ + fru(motherboard/memory-controller/dram-channel/dimm), \ + "dimm-size") && \ + confprop(fru(motherboard/memory-controller/dram-channel/dimm), \ + "dimm-size") == dimm_size && CE_PGFLTS > fault_rate} (1)-> \ + ereport.cpu.intel.nb.mem_ce@ \ + motherboard/memory-controller/dram-channel/dimm/rank; + +DIMM_CE(eight_g, "8G", 8, 1week, 2000) +DIMM_CE(four_g, "4G", 4, 1week, 1500) +DIMM_CE(two_g, "2G", 4, 2week, 1000) +DIMM_CE(one_g, "1G", 4, 4week, 500) +DIMM_CE(half_g, "512M", 4, 8week, 250) +DIMM_CE(quarter_g, "256M", 4, 16week, 125) + +engine serd.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + N=DIMM_CE_COUNT, T=DIMM_CE_TIME, method=persistent, + trip=ereport.memory.intel.dimm_trip@ + motherboard/memory-controller/dram-channel/dimm/rank; +event upset.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank, + engine=serd.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank; +prop upset.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank + {!confprop_defined(fru(motherboard/memory-controller/dram-channel/dimm), + "dimm-size") && CE_PGFLTS > 512} (1)-> + ereport.cpu.intel.nb.mem_ce@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop fault.memory.intel.dimm_ce@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.memory.intel.dimm_trip@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop upset.memory.discard@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.memory.intel.dimm_trip@ + motherboard/memory-controller/dram-channel/dimm/rank; + +event ereport.cpu.intel.nb.fbd.alert@ + motherboard/memory-controller/dram-channel/dimm/rank{within(12s)}; +event fault.memory.intel.fbd.alert@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=100, ASRU=motherboard/memory-controller/dram-channel/dimm/rank, + FRU=motherboard/memory-controller/dram-channel/dimm; + +prop fault.memory.intel.fbd.alert@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.cpu.intel.nb.fbd.alert@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop fault.memory.intel.fbd.alert@ + motherboard/memory-controller/dram-channel/dimm/rank (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.fbd.crc@ + motherboard/memory-controller/dram-channel/dimm/rank{within(12s)}; +event fault.memory.intel.fbd.crc@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=100, ASRU=motherboard/memory-controller/dram-channel/dimm/rank, + FRU=motherboard/memory-controller/dram-channel/dimm; + +prop fault.memory.intel.fbd.crc@ + motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.cpu.intel.nb.fbd.crc@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop fault.memory.intel.fbd.crc@ + motherboard/memory-controller/dram-channel/dimm/rank (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.fbd.reset_timeout@motherboard/memory-controller + {within(12s)}; +event fault.memory.intel.fbd.reset_timeout@motherboard/memory-controller, + FITrate=1000, ASRU=motherboard/memory-controller/dram-channel/dimm, + FRU=motherboard/memory-controller/dram-channel/dimm; + +prop fault.memory.intel.fbd.reset_timeout@motherboard/memory-controller (1)-> + ereport.cpu.intel.nb.fbd.reset_timeout@motherboard/memory-controller; + +prop fault.memory.intel.fbd.reset_timeout@motherboard/memory-controller (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel + {within(12s)}; +event fault.memory.intel.fbd.ch@motherboard/memory-controller/dram-channel, + FITrate=100, ASRU=motherboard/memory-controller/dram-channel/dimm, + FRU=motherboard/memory-controller/dram-channel/dimm; +event upset.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel, + engine=serd.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel; +event ereport.cpu.intel.nb.fbd_ch@motherboard/memory-controller/dram-channel + {within(12s)}; + +engine serd.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel, + N=2, T=1month, method=persistent, + trip=ereport.cpu.intel.nb.fbd_ch@motherboard/memory-controller/dram-channel; + +prop upset.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel (1)-> + ereport.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel; + +prop upset.cpu.intel.nb.fbd.ch@motherboard/memory-controller/dram-channel (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +prop fault.memory.intel.fbd.ch@ + motherboard/memory-controller/dram-channel (1)-> + ereport.cpu.intel.nb.fbd_ch@motherboard/memory-controller/dram-channel; + +event ereport.cpu.intel.nb.fbd.otf@motherboard/memory-controller/dram-channel + {within(12s)}; +event fault.memory.intel.fbd.otf@motherboard/memory-controller/dram-channel, + FITrate=100, ASRU=motherboard/memory-controller/dram-channel; +event upset.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel, + engine=serd.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel; +event ereport.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel + {within(12s)}; + +engine serd.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel, + N=2, T=1week, method=persistent, + trip=ereport.cpu.intel.nb.fbd_otf@ + motherboard/memory-controller/dram-channel; + +prop upset.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel (1)-> + ereport.cpu.intel.nb.fbd.otf@motherboard/memory-controller/dram-channel; + +prop fault.memory.intel.fbd.otf@ + motherboard/memory-controller/dram-channel (1)-> + ereport.cpu.intel.nb.fbd_otf@motherboard/memory-controller/dram-channel; + +event ereport.cpu.intel.nb.unknown@motherboard/memory-controller {within(12s)}; +event ereport.cpu.intel.nb.unknown@motherboard/memory-controller/dram-channel + {within(12s)}; +event upset.discard@motherboard/memory-controller; + +prop upset.discard@motherboard/memory-controller (0)-> + ereport.cpu.intel.nb.unknown@motherboard/memory-controller, + ereport.cpu.intel.nb.unknown@motherboard/memory-controller/dram-channel; + +event ereport.cpu.intel.nb.mem_ds@motherboard/memory-controller{within(30s)}; + +event fault.memory.intel.fbd.mem_ds@ + motherboard/memory-controller/dram-channel/dimm/rank, + FITrate=DIMM_UE_FIT, + ASRU=motherboard/memory-controller/dram-channel/dimm/rank, + FRU=motherboard/memory-controller/dram-channel/dimm; + +prop fault.memory.intel.fbd.mem_ds@ + motherboard/memory-controller/dram-channel/dimm/rank[rank_num] + { payloadprop_defined("rank") && rank_num == payloadprop("rank") } (1)-> + ereport.cpu.intel.nb.mem_ds@motherboard/memory-controller; + +event ereport.cpu.intel.nb.fsb@motherboard/chip{within(12s)}; +event fault.cpu.intel.nb.fsb@motherboard/chip, + FITrate=10000, ASRU=motherboard/chip, FRU=motherboard/chip; + +prop fault.cpu.intel.nb.fsb@motherboard/chip (1)-> + ereport.cpu.intel.nb.fsb@motherboard/chip; + +prop fault.cpu.intel.nb.fsb@motherboard/chip (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.ie@motherboard{within(12s)}; +event fault.cpu.intel.nb.ie@motherboard, + FITrate=10000, ASRU=motherboard, FRU=motherboard; + +prop fault.cpu.intel.nb.ie@motherboard (1)-> + ereport.cpu.intel.nb.ie@motherboard; + +prop fault.cpu.intel.nb.ie@motherboard (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.dma@motherboard{within(12s)}; +event fault.cpu.intel.nb.dma@motherboard, + FITrate=10000, ASRU=motherboard; + +prop fault.cpu.intel.nb.dma@motherboard (1)-> + ereport.cpu.intel.nb.dma@motherboard; + +prop fault.cpu.intel.nb.dma@motherboard (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.esi@motherboard{within(12s)}; +event ereport.cpu.intel.nb.pex@motherboard/hostbridge{within(12s)}; +event upset.cpu.intel.nb.pex@motherboard/hostbridge; + +prop upset.cpu.intel.nb.pex@motherboard/hostbridge (1)-> + ereport.cpu.intel.nb.esi@motherboard, + ereport.cpu.intel.nb.pex@motherboard/hostbridge; + +prop upset.cpu.intel.nb.pex@motherboard/hostbridge (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + +event ereport.cpu.intel.nb.unknown@ + motherboard/memory-controller/dram-channel/dimm/rank{within(12s)}; +event upset.discard@motherboard/memory-controller/dram-channel/dimm/rank; + +prop upset.discard@motherboard/memory-controller/dram-channel/dimm/rank (1)-> + ereport.cpu.intel.nb.unknown@ + motherboard/memory-controller/dram-channel/dimm/rank; + +prop upset.discard@motherboard/memory-controller/dram-channel/dimm/rank (0)-> + ereport.cpu.intel.bus_interconnect_memory_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_uc@chip/cpu, + ereport.cpu.intel.bus_interconnect_memory@chip/cpu, + ereport.cpu.intel.bus_interconnect@chip/cpu, + ereport.cpu.intel.external@chip/cpu; + diff --git a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c index aaea1b82cf..bec3aefd4d 100644 --- a/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c +++ b/usr/src/cmd/fm/modules/common/cpumem-retire/cma_main.c @@ -70,6 +70,14 @@ static const cma_subscriber_t cma_subrs[] = { cma_page_retire }, { "fault.memory.page_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, cma_page_retire }, + { "fault.memory.generic-x86.page_ce", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, cma_page_retire }, + { "fault.memory.generic-x86.page_ue", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, cma_page_retire }, + { "fault.memory.intel.page_ce", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, cma_page_retire }, + { "fault.memory.intel.page_ue", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, cma_page_retire }, { "fault.memory.dimm", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, NULL }, { "fault.memory.dimm_sb", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, @@ -78,6 +86,16 @@ static const cma_subscriber_t cma_subrs[] = { NULL }, { "fault.memory.dimm_ue", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, NULL }, + { "fault.memory.generic-x86.dimm_ce", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, NULL }, + { "fault.memory.generic-x86.dimm_ue", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, NULL }, + { "fault.memory.intel.dimm_ce", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, NULL }, + { "fault.memory.intel.dimm_ue", FM_FMRI_SCHEME_MEM, + FM_MEM_SCHEME_VERSION, NULL }, + { "fault.memory.intel.fbd.*", FM_FMRI_SCHEME_HC, + FM_HC_SCHEME_VERSION, NULL }, { "fault.memory.dimm_testfail", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, NULL }, { "fault.memory.bank", FM_FMRI_SCHEME_MEM, FM_MEM_SCHEME_VERSION, @@ -102,6 +120,27 @@ static const cma_subscriber_t cma_subrs[] = { FM_CPU_SCHEME_VERSION, NULL }, { "fault.cpu.amd.dramchannel", FM_FMRI_SCHEME_HC, FM_HC_SCHEME_VERSION, NULL }, + { "fault.cpu.generic-x86.bus_interconnect_memory", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.generic-x86.bus_interconnect_io", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.generic-x86.bus_interconnect", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.bus_interconnect_memory", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.bus_interconnect_io", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.bus_interconnect", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.nb.*", FM_FMRI_SCHEME_HC, + FM_HC_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.dma", FM_FMRI_SCHEME_HC, + FM_HC_SCHEME_VERSION, NULL }, + { "fault.cpu.intel.dma", FM_FMRI_SCHEME_CPU, + FM_CPU_SCHEME_VERSION, NULL }, + /* + * Default "fault.cpu.*" for "mem" scheme ASRU dispatch. + */ { "fault.cpu.*", FM_FMRI_SCHEME_CPU, FM_CPU_SCHEME_VERSION, cma_cpu_retire }, { NULL, NULL, 0, NULL } diff --git a/usr/src/cmd/mdb/i86pc/modules/Makefile b/usr/src/cmd/mdb/i86pc/modules/Makefile index e65e64d422..9bcdccd4af 100644 --- a/usr/src/cmd/mdb/i86pc/modules/Makefile +++ b/usr/src/cmd/mdb/i86pc/modules/Makefile @@ -2,9 +2,8 @@ # 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. +# Common Development and Distribution License (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. @@ -20,13 +19,14 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" SUBDIRS = \ amd_opteron \ + generic_cpu \ pcplusmp \ uppc \ unix diff --git a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/amd64/Makefile b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/amd64/Makefile index d4dcef0c55..6e9d6e0fae 100644 --- a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/amd64/Makefile +++ b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/amd64/Makefile @@ -2,9 +2,8 @@ # 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. +# Common Development and Distribution License (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. @@ -20,12 +19,12 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" -MODULE = cpu.AuthenticAMD.15.so +MODULE = cpu_ms.AuthenticAMD.15.so MDBTGT = kvm MODSRCS = ao.c diff --git a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c index e4a5ba03b0..289c2251ae 100644 --- a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c +++ b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ao.c @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -361,141 +361,7 @@ ao_mci_mask(uintptr_t val, uint_t flags, int argc, const mdb_arg_t *argv) return (ao_mci_ctlmask_common(val, flags, argc, argv, AO_MCI_MASK)); } -/*ARGSUSED3*/ -static int -ao_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) -{ - static const char *const whatstrs[] = { - "cyc-err", "poke-err", "unfault" - }; - - ao_mca_poll_trace_t mpt; - const char *what; - - if (argc != 0 || !(flags & DCMD_ADDRSPEC)) - return (DCMD_USAGE); - - if (mdb_vread(&mpt, sizeof (mpt), addr) != sizeof (mpt)) { - mdb_warn("failed to read ao_mca_poll_trace_t at %p", addr); - return (DCMD_ERR); - } - - if (DCMD_HDRSPEC(flags)) { - mdb_printf("%<u>%?s%</u> %<u>%?s%</u> %<u>%9s%</u> " - "%<u>%4s%</u>\n", "ADDR", "WHEN", "WHAT", "NERR"); - } - - if (mpt.mpt_what < sizeof (whatstrs) / sizeof (char *)) - what = whatstrs[mpt.mpt_what]; - else - what = "???"; - - mdb_printf("%?p %?p %9s %4u\n", addr, mpt.mpt_when, what, - mpt.mpt_nerr); - - return (DCMD_OK); -} - -typedef struct mptwalk_data { - uintptr_t mw_traceaddr; - ao_mca_poll_trace_t *mw_trace; - size_t mw_tracesz; - uint_t mw_tracenent; - uint_t mw_curtrace; -} mptwalk_data_t; - -static int -ao_mptwalk_init(mdb_walk_state_t *wsp) -{ - ao_mca_poll_trace_t *mpt; - mptwalk_data_t *mw; - GElf_Sym sym; - uint_t nent, i; - hrtime_t latest; - - if (wsp->walk_addr == NULL) { - mdb_warn("the address of a poll trace array must be specified"); - return (WALK_ERR); - } - - if (mdb_lookup_by_name("ao_mca_poll_trace_nent", &sym) < 0 || - sym.st_size != sizeof (uint_t) || mdb_vread(&nent, sizeof (uint_t), - sym.st_value) != sizeof (uint_t)) { - mdb_warn("failed to read ao_mca_poll_trace_nent from kernel"); - return (WALK_ERR); - } - - mw = mdb_alloc(sizeof (mptwalk_data_t), UM_SLEEP); - mw->mw_traceaddr = wsp->walk_addr; - mw->mw_tracenent = nent; - mw->mw_tracesz = nent * sizeof (ao_mca_poll_trace_t); - mw->mw_trace = mdb_alloc(mw->mw_tracesz, UM_SLEEP); - - if (mdb_vread(mw->mw_trace, mw->mw_tracesz, wsp->walk_addr) != - mw->mw_tracesz) { - mdb_free(mw->mw_trace, mw->mw_tracesz); - mdb_free(mw, sizeof (mptwalk_data_t)); - mdb_warn("failed to read poll trace array from kernel"); - return (WALK_ERR); - } - - latest = 0; - mw->mw_curtrace = 0; - for (mpt = mw->mw_trace, i = 0; i < mw->mw_tracenent; i++, mpt++) { - if (mpt->mpt_when > latest) { - latest = mpt->mpt_when; - mw->mw_curtrace = i; - } - } - - if (latest == 0) { - mdb_free(mw->mw_trace, mw->mw_tracesz); - mdb_free(mw, sizeof (mptwalk_data_t)); - return (WALK_DONE); /* trace array is empty */ - } - - wsp->walk_data = mw; - - return (WALK_NEXT); -} - -static int -ao_mptwalk_step(mdb_walk_state_t *wsp) -{ - mptwalk_data_t *mw = wsp->walk_data; - ao_mca_poll_trace_t *thismpt, *prevmpt; - int prev, rv; - - thismpt = &mw->mw_trace[mw->mw_curtrace]; - - rv = wsp->walk_callback(mw->mw_traceaddr + (mw->mw_curtrace * - sizeof (ao_mca_poll_trace_t)), thismpt, wsp->walk_cbdata); - - if (rv != WALK_NEXT) - return (rv); - - prev = (mw->mw_curtrace - 1) % mw->mw_tracenent; - prevmpt = &mw->mw_trace[prev]; - - if (prevmpt->mpt_when == 0 || prevmpt->mpt_when > thismpt->mpt_when) - return (WALK_DONE); - - mw->mw_curtrace = prev; - - return (WALK_NEXT); -} - -static void -ao_mptwalk_fini(mdb_walk_state_t *wsp) -{ - mptwalk_data_t *mw = wsp->walk_data; - - mdb_free(mw->mw_trace, mw->mw_tracesz); - mdb_free(mw, sizeof (mptwalk_data_t)); -} - static const mdb_dcmd_t dcmds[] = { - { "ao_poll_trace", ":", "dump a poll trace buffer", ao_mpt_dump }, { "ao_nbcfg", ":", "decode Northbridge config bits", ao_nbcfg_describe }, { "ao_scrubctl", ":", "decode Scrub Control Register", @@ -510,8 +376,6 @@ static const mdb_dcmd_t dcmds[] = { }; static const mdb_walker_t walkers[] = { - { "ao_poll_trace", "walks poll trace buffers in reverse chronological " - "order", ao_mptwalk_init, ao_mptwalk_step, ao_mptwalk_fini }, { NULL } }; diff --git a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ia32/Makefile b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ia32/Makefile index 9f6cd0fe8e..7799ee43f5 100644 --- a/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ia32/Makefile +++ b/usr/src/cmd/mdb/i86pc/modules/amd_opteron/ia32/Makefile @@ -2,9 +2,8 @@ # 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. +# Common Development and Distribution License (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. @@ -20,12 +19,12 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" -MODULE = cpu.AuthenticAMD.15.so +MODULE = cpu_ms.AuthenticAMD.15.so MDBTGT = kvm MODSRCS = ao.c diff --git a/usr/src/cmd/mdb/i86pc/modules/generic_cpu/Makefile b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/Makefile new file mode 100644 index 0000000000..00da5ed565 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/Makefile.master +SUBDIRS = ia32 +$(BUILD64)SUBDIRS += $(MACH64) +include ../../../Makefile.subdirs diff --git a/usr/src/cmd/mdb/i86pc/modules/generic_cpu/amd64/Makefile b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/amd64/Makefile new file mode 100644 index 0000000000..5317c7a339 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/amd64/Makefile @@ -0,0 +1,40 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +MODULE = cpu.generic.so +MDBTGT = kvm + +MODSRCS = gcpu.c + +include ../../../../../Makefile.cmd +include ../../../../../Makefile.cmd.64 +include ../../../../intel/Makefile.amd64 +include ../../../Makefile.i86pc +include ../../../../Makefile.module + +CPPFLAGS += -I../../../../common +CPPFLAGS += -I$(SRC)/uts/i86pc/cpu +CPPFLAGS += -I$(SRC)/uts/intel +CPPFLAGS += -I$(SRC)/uts/i86pc diff --git a/usr/src/cmd/mdb/i86pc/modules/generic_cpu/gcpu.c b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/gcpu.c new file mode 100644 index 0000000000..35370eeee3 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/gcpu.c @@ -0,0 +1,590 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <mdb/mdb_modapi.h> +#include <generic_cpu/gcpu.h> +#include <sys/cpu_module_impl.h> +#include <sys/cpu_module_ms_impl.h> + +typedef struct cmi_hdl_impl { + enum cmi_hdl_class cmih_class; /* Handle nature */ + struct cmi_hdl_ops *cmih_ops; /* Operations vector */ + uint_t cmih_chipid; /* Chipid of cpu resource */ + uint_t cmih_coreid; /* Core within die */ + uint_t cmih_strandid; /* Thread within core */ + volatile uint32_t *cmih_refcntp; /* Reference count pointer */ + uint64_t cmih_msrsrc; /* MSR data source flags */ + void *cmih_hdlpriv; /* cmi_hw.c private data */ + void *cmih_spec; /* cmi_hdl_{set,get}_specific */ + void *cmih_cmi; /* cpu mod control structure */ + void *cmih_cmidata; /* cpu mod private data */ + const struct cmi_mc_ops *cmih_mcops; /* Memory-controller ops */ + void *cmih_mcdata; /* Memory-controller data */ +} cmi_hdl_impl_t; + +struct cmi_hdl_hashent { + volatile uint32_t cmhe_refcnt; + cmi_hdl_impl_t *cmhe_hdlp; +}; + +typedef struct cmi { + struct cmi *cmi_next; + struct cmi *cmi_prev; + const cmi_ops_t *cmi_ops; + struct modctl *cmi_modp; + uint_t cmi_refcnt; +} cmi_t; + +typedef struct cms { + struct cms *cms_next; + struct cms *cms_prev; + const cms_ops_t *cms_ops; + struct modctl *cms_modp; + uint_t cms_refcnt; +} cms_t; + +struct cms_ctl { + cms_t *cs_cms; + void *cs_cmsdata; +}; + +#define CMI_MAX_CHIPS 16 +#define CMI_MAX_CORES_PER_CHIP 4 +#define CMI_MAX_STRANDS_PER_CORE 2 +#define CMI_HDL_HASHSZ (CMI_MAX_CHIPS * CMI_MAX_CORES_PER_CHIP * \ + CMI_MAX_STRANDS_PER_CORE) + +struct cmih_walk_state { + int idx; + struct cmi_hdl_hashent *hdlhash; +}; + +static int +cmih_walk_init(mdb_walk_state_t *wsp) +{ + size_t sz = CMI_HDL_HASHSZ * sizeof (struct cmi_hdl_hashent); + struct cmih_walk_state *hwsp; + uintptr_t addr; + + if (wsp->walk_addr != NULL) { + mdb_warn("cmi_hdl is a global walker\n"); + return (WALK_ERR); + } + + if (mdb_readvar(&addr, "cmi_hdl_hash") == -1) { + mdb_warn("read of cmi_hdl_hash failed"); + return (WALK_ERR); + } else if (addr == NULL) { + return (WALK_DONE); + } + + wsp->walk_data = hwsp = + mdb_zalloc(sizeof (struct cmih_walk_state), UM_SLEEP); + hwsp->hdlhash = mdb_alloc(sz, UM_SLEEP); + + if (mdb_vread(hwsp->hdlhash, sz, addr) != sz) { + mdb_warn("read of cmi_hdl_hash array at 0x%p failed", addr); + return (WALK_ERR); + } + + wsp->walk_addr = + (uintptr_t)((struct cmi_hdl_hashent *)wsp->walk_data)->cmhe_hdlp; + + return (WALK_NEXT); +} + +static int +cmih_walk_step(mdb_walk_state_t *wsp) +{ + struct cmih_walk_state *hwsp = wsp->walk_data; + uintptr_t addr = (uintptr_t)(hwsp->hdlhash)[hwsp->idx].cmhe_hdlp; + cmi_hdl_impl_t hdl; + int rv; + + if (wsp->walk_addr == NULL || addr == NULL) + return (++hwsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE); + + if (mdb_vread(&hdl, sizeof (hdl), addr) != sizeof (hdl)) { + mdb_warn("read of handle at 0x%p failed", addr); + return (WALK_DONE); + } + + if ((rv = wsp->walk_callback(addr, (void *)&hdl, + wsp->walk_cbdata)) != WALK_NEXT) + return (rv); + + return (++hwsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE); +} + +static void +cmih_walk_fini(mdb_walk_state_t *wsp) +{ + struct cmih_walk_state *hwsp = wsp->walk_data; + + if (hwsp != NULL) { + if (hwsp->hdlhash != NULL) + mdb_free(hwsp->hdlhash, CMI_HDL_HASHSZ * + sizeof (struct cmih_walk_state)); + mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state)); + } +} + +struct cmihdl_cb { + int mod_cpuid; + int mod_chipid; + int mod_coreid; + int mod_strandid; + uintptr_t mod_hdladdr; +}; + +static int +cmihdl_cb(uintptr_t addr, const void *arg, void *data) +{ + cmi_hdl_impl_t *hdl = (cmi_hdl_impl_t *)arg; + struct cmihdl_cb *cbp = data; + cpu_t *cp; + int rv; + + if (cbp->mod_cpuid != -1) { + cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP); + if (mdb_vread(cp, sizeof (cpu_t), + (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) { + mdb_warn("Read of cpu_t at 0x%p failed", + hdl->cmih_hdlpriv); + mdb_free(cp, sizeof (cpu_t)); + return (WALK_ERR); + } + + if (cp->cpu_id == cbp->mod_cpuid) { + cbp->mod_hdladdr = addr; + rv = WALK_DONE; + } else { + rv = WALK_NEXT; + } + + mdb_free(cp, sizeof (cpu_t)); + return (rv); + } else { + if (hdl->cmih_chipid == cbp->mod_chipid && + hdl->cmih_coreid == cbp->mod_coreid && + hdl->cmih_strandid == cbp->mod_strandid) { + cbp->mod_hdladdr = addr; + return (WALK_DONE); + } else { + return (WALK_NEXT); + } + } +} + +static int +cmihdl_disp(uintptr_t addr, cmi_hdl_impl_t *hdl) +{ + struct cms_ctl cmsctl; /* 16 bytes max */ + struct modctl cmimodc, cmsmodc; /* 288 bytes max */ + cmi_t cmi; /* 40 bytes max */ + cms_t cms; /* 40 bytes max */ + cpu_t *cp; + char cmimodnm[25], cmsmodnm[25]; /* 50 bytes */ + char cpuidstr[4], hwidstr[16]; + int native = hdl->cmih_class == CMI_HDL_NATIVE; + uint32_t refcnt; + + cmimodnm[0] = cmsmodnm[0] = '-'; + cmimodnm[1] = cmsmodnm[1] = '\0'; + + if (hdl->cmih_cmi != NULL) { + if (mdb_vread(&cmi, sizeof (cmi_t), + (uintptr_t)hdl->cmih_cmi) != sizeof (cmi)) { + mdb_warn("Read of cmi_t at 0x%p failed", + hdl->cmih_cmi); + return (0); + } + + if (cmi.cmi_modp != NULL) { + if (mdb_vread(&cmimodc, sizeof (struct modctl), + (uintptr_t)cmi.cmi_modp) != sizeof (cmimodc)) { + mdb_warn("Read of modctl at 0x%p failed", + cmi.cmi_modp); + return (0); + } + + if (mdb_readstr(cmimodnm, sizeof (cmimodnm), + (uintptr_t)cmimodc.mod_modname) == -1) { + mdb_warn("Read of cmi module name at 0x%p " + "failed", cmimodc.mod_modname); + return (0); + } + } + } + + if (hdl->cmih_spec != NULL) { + if (mdb_vread(&cmsctl, sizeof (struct cms_ctl), + (uintptr_t)hdl->cmih_spec) != sizeof (cmsctl)) { + mdb_warn("Read of struct cms_ctl at 0x%p failed", + hdl->cmih_spec); + return (0); + } + + if (mdb_vread(&cms, sizeof (cms_t), + (uintptr_t)cmsctl.cs_cms) != sizeof (cms)) { + mdb_warn("Read of cms_t at 0x%p failed", cmsctl.cs_cms); + return (0); + } + + if (cms.cms_modp != NULL) { + if (mdb_vread(&cmsmodc, sizeof (struct modctl), + (uintptr_t)cms.cms_modp) != sizeof (cmsmodc)) { + mdb_warn("Read of modctl at 0x%p failed", + cms.cms_modp); + return (0); + } + + if (mdb_readstr(cmsmodnm, sizeof (cmsmodnm), + (uintptr_t)cmsmodc.mod_modname) == -1) { + mdb_warn("Read of cms module name at 0x%p " + "failed", cmsmodc.mod_modname); + return (0); + } + } + } + + if (mdb_vread(&refcnt, sizeof (uint32_t), + (uintptr_t)hdl->cmih_refcntp) != sizeof (uint32_t)) { + mdb_warn("Read of reference count for hdl 0x%p failed", hdl); + return (0); + } + + if (native) { + cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP); + + if (mdb_vread(cp, sizeof (cpu_t), + (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) { + mdb_free(cp, sizeof (cpu_t)); + mdb_warn("Read of cpu_t at 0x%p failed", + hdl->cmih_hdlpriv); + return (0); + } + } + + if (native) { + (void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "%d", + cp->cpu_id); + } else { + (void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "-"); + } + + (void) mdb_snprintf(hwidstr, sizeof (hwidstr), "%d/%d/%d", + hdl->cmih_chipid, hdl->cmih_coreid, hdl->cmih_strandid); + + mdb_printf("%16lx %3d %3s %8s %2s %-13s %-24s\n", addr, + refcnt, cpuidstr, hwidstr, hdl->cmih_mcops ? "Y" : "N", + cmimodnm, cmsmodnm); + + if (native) + mdb_free(cp, sizeof (cpu_t)); + + return (1); +} + +#define HDRFMT "%-16s %3s %3s %8s %2s %-13s %-24s\n" + +static int +cmihdl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + struct cmihdl_cb cb; + cmi_hdl_impl_t *hdl; + + /* + * If an address is given it must be that of a cmi handle. + * Otherwise if the user has specified -c <cpuid> or + * -c <chipid/coreid/strandid> we will lookup a matching handle. + * Otherwise we'll walk and callback to this dcmd. + */ + if (!(flags & DCMD_ADDRSPEC)) { + char *p, *buf; + int len; + + if (argc == 0) + return (mdb_walk_dcmd("cmihdl", "cmihdl", argc, + argv) == 0 ? DCMD_OK : DCMD_ERR); + + + if (mdb_getopts(argc, argv, + 'c', MDB_OPT_STR, &p, + NULL) != argc) + return (DCMD_USAGE); + + if ((len = strlen(p)) == 0) { + return (DCMD_USAGE); + } else { + buf = mdb_alloc(len + 1, UM_SLEEP); + strcpy(buf, p); + } + + cb.mod_cpuid = cb.mod_chipid = cb.mod_coreid = + cb.mod_strandid = -1; + + if ((p = strchr(buf, '/')) == NULL) { + /* Native cpuid */ + cb.mod_cpuid = (int)mdb_strtoull(buf); + } else { + /* Comma-separated triplet chip,core,strand. */ + char *q = buf; + + *p = '\0'; + cb.mod_chipid = (int)mdb_strtoull(q); + + if ((q = p + 1) >= buf + len || + (p = strchr(q, '/')) == NULL) { + mdb_free(buf, len); + return (DCMD_USAGE); + } + + *p = '\0'; + cb.mod_coreid = (int)mdb_strtoull(q); + + if ((q = p + 1) >= buf + len) { + mdb_free(buf, len); + return (DCMD_USAGE); + } + + cb.mod_strandid = (int)mdb_strtoull(q); + } + + mdb_free(buf, len); + + cb.mod_hdladdr = NULL; + if (mdb_walk("cmihdl", cmihdl_cb, &cb) == -1) { + mdb_warn("cmi_hdl walk failed\n"); + return (DCMD_ERR); + } + + if (cb.mod_hdladdr == NULL) { + if (cb.mod_cpuid != -1) { + mdb_warn("No handle found for cpuid %d\n", + cb.mod_cpuid); + } else { + + mdb_warn("No handle found for chip %d " + "core %d strand %d\n", cb.mod_chipid, + cb.mod_coreid, cb.mod_strandid); + } + return (DCMD_ERR); + } + + addr = cb.mod_hdladdr; + } + + if (DCMD_HDRSPEC(flags)) { + char ul[] = "----------------------------"; + char *p = ul + sizeof (ul) - 1; + + mdb_printf(HDRFMT HDRFMT, + "HANDLE", "REF", "CPU", "CH/CR/ST", "MC", + "MODULE", "MODEL-SPECIFIC", + p - 16, p - 3, p - 3, p - 8, p - 2, p - 13, p - 24); + } + + hdl = mdb_alloc(sizeof (cmi_hdl_impl_t), UM_SLEEP); + + if (mdb_vread(hdl, sizeof (cmi_hdl_impl_t), addr) != + sizeof (cmi_hdl_impl_t)) { + mdb_free(hdl, sizeof (cmi_hdl_impl_t)); + mdb_warn("Read of cmi handle at 0x%p failed", addr); + return (DCMD_ERR); + } + + if (!cmihdl_disp(addr, hdl)) { + mdb_free(hdl, sizeof (cmi_hdl_impl_t)); + return (DCMD_ERR); + } + + mdb_free(hdl, sizeof (cmi_hdl_impl_t)); + + return (DCMD_OK); +} + +/*ARGSUSED*/ +static int +gcpu_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + static const char *const whatstrs[] = { + "cyc-err", /* GCPU_MPT_WHAT_CYC_ERR */ + "poke-err", /* GCPU_MPT_WHAT_POKE_ERR */ + "unfault", /* GCPU_MPT_WHAT_UNFAULTING */ + }; + + gcpu_mca_poll_trace_t mpt; + const char *what; + + if (argc != 0 || !(flags & DCMD_ADDRSPEC)) + return (DCMD_USAGE); + + if (mdb_vread(&mpt, sizeof (mpt), addr) != sizeof (mpt)) { + mdb_warn("failed to read gcpu_mca_poll_trace_t at 0x%p", addr); + return (DCMD_ERR); + } + + if (DCMD_HDRSPEC(flags)) { + mdb_printf("%<u>%?s%</u> %<u>%?s%</u> %<u>%9s%</u> " + "%<u>%4s%</u>\n", "ADDR", "WHEN", "WHAT", "NERR"); + } + + if (mpt.mpt_what < sizeof (whatstrs) / sizeof (char *)) + what = whatstrs[mpt.mpt_what]; + else + what = "???"; + + mdb_printf("%?p %?p %9s %4u\n", addr, mpt.mpt_when, what, + mpt.mpt_nerr); + + return (DCMD_OK); +} + +typedef struct mptwalk_data { + uintptr_t mw_traceaddr; + gcpu_mca_poll_trace_t *mw_trace; + size_t mw_tracesz; + uint_t mw_tracenent; + uint_t mw_curtrace; +} mptwalk_data_t; + +static int +gcpu_mptwalk_init(mdb_walk_state_t *wsp) +{ + gcpu_mca_poll_trace_t *mpt; + mptwalk_data_t *mw; + GElf_Sym sym; + uint_t nent, i; + hrtime_t latest; + + if (wsp->walk_addr == NULL) { + mdb_warn("the address of a poll trace array must be " + "specified\n"); + return (WALK_ERR); + } + + if (mdb_lookup_by_name("gcpu_mca_poll_trace_nent", &sym) < 0 || + sym.st_size != sizeof (uint_t) || mdb_vread(&nent, sizeof (uint_t), + sym.st_value) != sizeof (uint_t)) { + mdb_warn("failed to read gcpu_mca_poll_trace_nent from kernel"); + return (WALK_ERR); + } + + mw = mdb_alloc(sizeof (mptwalk_data_t), UM_SLEEP); + mw->mw_traceaddr = wsp->walk_addr; + mw->mw_tracenent = nent; + mw->mw_tracesz = nent * sizeof (gcpu_mca_poll_trace_t); + mw->mw_trace = mdb_alloc(mw->mw_tracesz, UM_SLEEP); + + if (mdb_vread(mw->mw_trace, mw->mw_tracesz, wsp->walk_addr) != + mw->mw_tracesz) { + mdb_free(mw->mw_trace, mw->mw_tracesz); + mdb_free(mw, sizeof (mptwalk_data_t)); + mdb_warn("failed to read poll trace array from kernel"); + return (WALK_ERR); + } + + latest = 0; + mw->mw_curtrace = 0; + for (mpt = mw->mw_trace, i = 0; i < mw->mw_tracenent; i++, mpt++) { + if (mpt->mpt_when > latest) { + latest = mpt->mpt_when; + mw->mw_curtrace = i; + } + } + + if (latest == 0) { + mdb_free(mw->mw_trace, mw->mw_tracesz); + mdb_free(mw, sizeof (mptwalk_data_t)); + return (WALK_DONE); /* trace array is empty */ + } + + wsp->walk_data = mw; + + return (WALK_NEXT); +} + +static int +gcpu_mptwalk_step(mdb_walk_state_t *wsp) +{ + mptwalk_data_t *mw = wsp->walk_data; + gcpu_mca_poll_trace_t *thismpt, *prevmpt; + int prev, rv; + + thismpt = &mw->mw_trace[mw->mw_curtrace]; + + rv = wsp->walk_callback(mw->mw_traceaddr + (mw->mw_curtrace * + sizeof (gcpu_mca_poll_trace_t)), thismpt, wsp->walk_cbdata); + + if (rv != WALK_NEXT) + return (rv); + + prev = (mw->mw_curtrace - 1) % mw->mw_tracenent; + prevmpt = &mw->mw_trace[prev]; + + if (prevmpt->mpt_when == 0 || prevmpt->mpt_when > thismpt->mpt_when) + return (WALK_DONE); + + mw->mw_curtrace = prev; + + return (WALK_NEXT); +} + +static void +gcpu_mptwalk_fini(mdb_walk_state_t *wsp) +{ + mptwalk_data_t *mw = wsp->walk_data; + + mdb_free(mw->mw_trace, mw->mw_tracesz); + mdb_free(mw, sizeof (mptwalk_data_t)); +} + +static const mdb_dcmd_t dcmds[] = { + { "cmihdl", ": -c <cpuid>|<chip,core,strand> ", + "dump a cmi_handle_t", cmihdl }, + { "gcpu_poll_trace", ":", "dump a poll trace buffer", gcpu_mpt_dump }, + { NULL } +}; + +static const mdb_walker_t walkers[] = { + { "cmihdl", "walks cpu module interface handle list", + cmih_walk_init, cmih_walk_step, cmih_walk_fini, NULL }, + { "gcpu_poll_trace", "walks poll trace buffers in reverse " + "chronological order", gcpu_mptwalk_init, gcpu_mptwalk_step, + gcpu_mptwalk_fini, NULL }, + { NULL } +}; + +static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; + +const mdb_modinfo_t * +_mdb_init(void) +{ + return (&modinfo); +} diff --git a/usr/src/cmd/mdb/i86pc/modules/generic_cpu/ia32/Makefile b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/ia32/Makefile new file mode 100644 index 0000000000..aa34fdd177 --- /dev/null +++ b/usr/src/cmd/mdb/i86pc/modules/generic_cpu/ia32/Makefile @@ -0,0 +1,40 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +MODULE = cpu.generic.so +MDBTGT = kvm + +MODSRCS = gcpu.c + +include ../../../../../Makefile.cmd +include ../../../../intel/Makefile.ia32 +include ../../../Makefile.i86pc +include ../../../../Makefile.module + +CPPFLAGS += -I../../../../common +CPPFLAGS += -I$(SRC)/uts/i86pc/cpu +CPPFLAGS += -I$(SRC)/uts/intel +CPPFLAGS += -I$(SRC)/uts/i86pc diff --git a/usr/src/common/mc/mc-amd/mcamd_patounum.c b/usr/src/common/mc/mc-amd/mcamd_patounum.c index 594af7e7cc..52ecce05e7 100644 --- a/usr/src/common/mc/mc-amd/mcamd_patounum.c +++ b/usr/src/common/mc/mc-amd/mcamd_patounum.c @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -263,27 +263,28 @@ unum_fill(struct mcamd_hdl *hdl, mcamd_node_t *cs, int which, } unump->unum_board = 0; - unump->unum_chip = chipnum; + unump->unum_chip = (int)chipnum; unump->unum_mc = 0; - unump->unum_cs = csnum; - unump->unum_rank = ranknum; + unump->unum_chan = MC_INVALNUM; + unump->unum_cs = (int)csnum; + unump->unum_rank = (int)ranknum; for (i = 0; i < MC_UNUM_NDIMM; i++) { unump->unum_dimms[i] = MC_INVALNUM; } switch (which) { case CSDIMM1: - unump->unum_dimms[0] = dimm1; - offsetdimm = dimm1; + unump->unum_dimms[0] = (int)dimm1; + offsetdimm = (int)dimm1; break; case CSDIMM2: - unump->unum_dimms[0] = dimm2; - offsetdimm = dimm2; + unump->unum_dimms[0] = (int)dimm2; + offsetdimm = (int)dimm2; break; case CSDIMM1 | CSDIMM2: - unump->unum_dimms[0] = dimm1; - unump->unum_dimms[1] = dimm2; - offsetdimm = dimm1; + unump->unum_dimms[0] = (int)dimm1; + unump->unum_dimms[1] = (int)dimm2; + offsetdimm = (int)dimm1; break; } @@ -528,7 +529,7 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa, } } - if (!MC_REV_MATCH(rev, MC_REVS_FG)) + if (!MC_REV_MATCH(rev, MC_F_REVS_FG)) InputAddr <<= 4; for (cs = mcamd_cs_next(hdl, mc, NULL); cs != NULL; @@ -556,7 +557,7 @@ mc_bkdg_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa, * CSBase = Register already read * CSEn = We only keep enabled cs. */ - if (MC_REV_MATCH(rev, MC_REVS_FG)) { + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { CSBase &= 0x1ff83fe0; /* CSMask = Get_PCI() Retrieved above */ CSMask = (CSMask | 0x0007c01f) & 0x1fffffff; @@ -667,6 +668,7 @@ mc_patounum(struct mcamd_hdl *hdl, mcamd_node_t *mc, uint64_t pa, } else if (!(unump->unum_board == bkdg_unum.unum_board && unump->unum_chip == bkdg_unum.unum_chip && unump->unum_mc == bkdg_unum.unum_mc && + unump->unum_chan == bkdg_unum.unum_chan && unump->unum_cs == bkdg_unum.unum_cs && unump->unum_dimms[0] == bkdg_unum.unum_dimms[0] && unump->unum_dimms[1] == bkdg_unum.unum_dimms[1])) { diff --git a/usr/src/common/mc/mc-amd/mcamd_rowcol.c b/usr/src/common/mc/mc-amd/mcamd_rowcol.c index 979a182937..d5c9709666 100644 --- a/usr/src/common/mc/mc-amd/mcamd_rowcol.c +++ b/usr/src/common/mc/mc-amd/mcamd_rowcol.c @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -94,7 +94,7 @@ getcsprops(struct mcamd_hdl *hdl, mcamd_node_t *cs, const char *caller, mcamd_dprintf(hdl, MCAMD_DBG_ERR, "%s: failed to read cs " "props for cs 0x%p\n", caller, cs); return (mcamd_set_errno(hdl, EMCAMD_TREEINVALID)); - } + } return (0); } @@ -452,7 +452,7 @@ mc_pa_to_offset(struct mcamd_hdl *hdl, mcamd_node_t *mc, mcamd_node_t *cs, offset_un.do_valid = 1; offset_un.do_version = MCAMD_OFFSET_VERSION; - offset_un.do_rank = csp.dimmrank; + offset_un.do_rank = (uint32_t)csp.dimmrank; offset_un.do_row = rowaddr; offset_un.do_bank = bankaddr; offset_un.do_col = coladdr; diff --git a/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c b/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c index 79e360590b..f12c59b746 100644 --- a/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c +++ b/usr/src/common/mc/mc-amd/mcamd_rowcol_tbl.c @@ -21,7 +21,7 @@ /* * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -46,9 +46,9 @@ static const struct _bnkaddrmode_tbldesc { int nmodes; const struct rct_bnkaddrmode *modetbl; } bnkaddr_tbls[] = { - { MC_REVS_BC, 7, bnkaddr_tbls_pre_d }, - { MC_REVS_DE, 11, bnkaddr_tbls_d_e }, - { MC_REVS_FG, 12, bnkaddr_tbls_f }, + { MC_F_REVS_BC, 7, bnkaddr_tbls_pre_d }, + { MC_F_REVS_DE, 11, bnkaddr_tbls_d_e }, + { MC_F_REVS_FG, 12, bnkaddr_tbls_f }, }; /* @@ -306,7 +306,7 @@ static const struct rct_bnkaddrmode bnkaddr_tbls_f[] = { * See BKDG 3.29 3.5.6 Table 7. */ static const struct _rcbmap_tbl dram_addrmap_pre_d_64 = { - MC_REVS_BC, + MC_F_REVS_BC, 64, { { /* 000 */ @@ -356,7 +356,7 @@ static const struct _rcbmap_tbl dram_addrmap_pre_d_64 = { * See BKDG 3.29 3.5.6 Table 8. */ static const struct _rcbmap_tbl dram_addrmap_pre_d_128 = { - MC_REVS_BC, + MC_F_REVS_BC, 128, { { /* 000 */ @@ -405,7 +405,7 @@ static const struct _rcbmap_tbl dram_addrmap_pre_d_128 = { * See BKDG 3.29 3.5.6 Table 9. */ static const struct _rcbmap_tbl dram_addrmap_d_e_64 = { - MC_REVS_DE, + MC_F_REVS_DE, 64, { { /* 0000 */ @@ -474,7 +474,7 @@ static const struct _rcbmap_tbl dram_addrmap_d_e_64 = { * See BKDG 3.29 3.5.6 Table 9. */ static const struct _rcbmap_tbl dram_addrmap_d_e_128 = { - MC_REVS_DE, + MC_F_REVS_DE, 128, { { /* 0000 */ @@ -542,7 +542,7 @@ static const struct _rcbmap_tbl dram_addrmap_d_e_128 = { * Row/Column/Bank address mappings for revs F/G in 64-bit mode, no interleave. */ static const struct _rcbmap_tbl dram_addrmap_f_64 = { - MC_REVS_FG, + MC_F_REVS_FG, 64, { { /* 0000 */ @@ -617,7 +617,7 @@ static const struct _rcbmap_tbl dram_addrmap_f_64 = { * Row/Column/Bank address mappings for revs F/G in 128-bit mode, no interleave. */ static const struct _rcbmap_tbl dram_addrmap_f_128 = { - MC_REVS_FG, + MC_F_REVS_FG, 128, { { /* 0000 */ @@ -705,7 +705,7 @@ static const struct _rcbmap_tbl dram_addrmap_f_128 = { */ static const struct _bnkswzl_tbl bnswzl_info_e_64 = { - MC_REV_E, + MC_F_REV_E, 64, { { @@ -717,7 +717,7 @@ static const struct _bnkswzl_tbl bnswzl_info_e_64 = { }; static const struct _bnkswzl_tbl bnswzl_info_e_128 = { - MC_REV_E, + MC_F_REV_E, 128, { { @@ -729,7 +729,7 @@ static const struct _bnkswzl_tbl bnswzl_info_e_128 = { }; static const struct _bnkswzl_tbl bnswzl_info_f_64 = { - MC_REVS_FG, + MC_F_REVS_FG, 64, { { @@ -741,7 +741,7 @@ static const struct _bnkswzl_tbl bnswzl_info_f_64 = { }; static const struct _bnkswzl_tbl bnswzl_info_f_128 = { - MC_REVS_FG, + MC_F_REVS_FG, 128, { { @@ -872,8 +872,8 @@ rct_csintlv_bits(uint_t mcrev, int width, uint_t csmode, int factor, * not implemented prior to rev F. */ if (factor == 8 && width == 128 && - ((MC_REV_MATCH(mcrev, MC_REVS_BC) && csmode == 0x6) || - (MC_REV_MATCH(mcrev, MC_REVS_DE) && + ((MC_REV_MATCH(mcrev, MC_F_REVS_BC) && csmode == 0x6) || + (MC_REV_MATCH(mcrev, MC_F_REVS_DE) && (csmode == 0x9 || csmode == 0xa)))) { csid->csi_factor = 0; return; diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_node.c b/usr/src/lib/fm/topo/libtopo/common/topo_node.c index 8973ee10b5..23e048724c 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_node.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_node.c @@ -403,8 +403,7 @@ topo_node_lookup(tnode_t *pnode, const char *name, topo_instance_t inst) int topo_node_hash(topo_nodehash_t *nhp, topo_instance_t inst) { - return (nhp->th_range.tr_max == 0 ? - nhp->th_range.tr_max : inst % (nhp->th_range.tr_max + 1)); + return ((inst - nhp->th_range.tr_min) % nhp->th_arrlen); } static tnode_t * diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c index d89ecbb1fa..b15dc170c2 100644 --- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c +++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c @@ -318,7 +318,8 @@ topo_search_path(topo_mod_t *mod, const char *rootdir, const char *file) * SMBIOS serial numbers can contain characters (particularly ':' and ' ') * that are invalid for the authority and can break FMRI parsing. We translate * any invalid characters to a safe '-', as well as trimming any leading or - * trailing whitespace. + * trailing whitespace. Similarly, '/' can be found in some product names + * so we translate that to '-'. */ char * topo_cleanup_auth_str(topo_hdl_t *thp, char *begin) @@ -344,7 +345,7 @@ topo_cleanup_auth_str(topo_hdl_t *thp, char *begin) return (NULL); (void) snprintf(buf, count, "%s", begin); - while ((str = strpbrk(buf, " :=")) != NULL) + while ((str = strpbrk(buf, " :=/")) != NULL) *str = '-'; pp = topo_hdl_strdup(thp, buf); diff --git a/usr/src/lib/fm/topo/maps/i86pc/chip-hc-topology.xml b/usr/src/lib/fm/topo/maps/i86pc/chip-hc-topology.xml index b0fe380ffa..631c96753e 100644 --- a/usr/src/lib/fm/topo/maps/i86pc/chip-hc-topology.xml +++ b/usr/src/lib/fm/topo/maps/i86pc/chip-hc-topology.xml @@ -31,7 +31,7 @@ <range name='chip' min='0' max='100'> <propset type='product' - set='Sun-Fire-V20z|Sun-Fire-V40z|W1100z/2100z|Sun-Ultra-20-Workstation|Ultra20-M2|Sun-Ultra-40-M2-Workstation'> + set='Sun-Fire-V20z|Sun-Fire-V40z|W1100z-2100z|Sun-Ultra-20-Workstation|Ultra20-M2|Sun-Ultra-40-M2-Workstation'> <propgroup name='protocol' version='1' name-stability='Private' data-stability='Private' > diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/Makefile b/usr/src/lib/fm/topo/modules/i86pc/chip/Makefile index 75b058a3ca..e78134895a 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/chip/Makefile +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/Makefile @@ -27,7 +27,7 @@ MODULE = chip ARCH = i86pc CLASS = arch -MODULESRCS = chip_label.c chip.c +MODULESRCS = chip.c chip_label.c chip_subr.c chip_amd.c chip_intel.c include ../../Makefile.plugin diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c index 0d9c0f77c6..1a0435c053 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.c @@ -35,7 +35,6 @@ #include <limits.h> #include <alloca.h> #include <kstat.h> -#include <fcntl.h> #include <errno.h> #include <libnvpair.h> #include <sys/types.h> @@ -46,14 +45,11 @@ #include <sys/systeminfo.h> #include <sys/mc.h> #include <sys/mc_amd.h> +#include <sys/mc_intel.h> #include <fm/topo_mod.h> #include "chip.h" -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - #define MAX_DIMMNUM 7 #define MAX_CSNUM 7 @@ -63,50 +59,20 @@ * possibly a memory controller) are constructed underneath. */ -static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, - topo_instance_t, void *, void *); - -static int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t, - nvlist_t *, nvlist_t **); +static int chip_enum(topo_mod_t *, tnode_t *, const char *, + topo_instance_t, topo_instance_t, void *, void *); static const topo_modops_t chip_ops = { chip_enum, NULL}; static const topo_modinfo_t chip_info = { CHIP_NODE_NAME, FM_FMRI_SCHEME_HC, CHIP_VERSION, &chip_ops }; -static const topo_pgroup_info_t cs_pgroup = - { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; -static const topo_pgroup_info_t dimm_pgroup = - { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; -static const topo_pgroup_info_t mc_pgroup = - { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; static const topo_pgroup_info_t chip_pgroup = { PGNAME(CHIP), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; static const topo_pgroup_info_t cpu_pgroup = { PGNAME(CPU), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; -static const topo_pgroup_info_t rank_pgroup = - { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; -static const topo_pgroup_info_t chan_pgroup = - { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; - -const topo_method_t rank_methods[] = { - { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, - TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, - mem_asru_compute }, - { NULL } -}; -const topo_method_t dimm_methods[] = { - { SIMPLE_DIMM_LBL, "Property method", 0, - TOPO_STABILITY_INTERNAL, simple_dimm_label}, - { SIMPLE_DIMM_LBL_MP, "Property method", 0, - TOPO_STABILITY_INTERNAL, simple_dimm_label_mp}, - { SEQ_DIMM_LBL, "Property method", 0, - TOPO_STABILITY_INTERNAL, seq_dimm_label}, - { NULL } -}; - -const topo_method_t chip_methods[] = { +static const topo_method_t chip_methods[] = { { SIMPLE_CHIP_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, simple_chip_label}, { G4_CHIP_LBL, "Property method", 0, @@ -114,24 +80,6 @@ const topo_method_t chip_methods[] = { { NULL } }; -static nvlist_t *cs_fmri[MC_CHIP_NCS]; - -static void -whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...) -{ - va_list ap; - char buf[160]; - - if (nerr != NULL) - ++*nerr; - - va_start(ap, fmt); - (void) vsnprintf(buf, sizeof (buf), fmt, ap); - va_end(ap); - - topo_mod_dprintf(mod, "%s", buf); -} - int _topo_init(topo_mod_t *mod) { @@ -189,114 +137,6 @@ _topo_fini(topo_mod_t *mod) } static int -add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, - const char *pgname, const char *pname) -{ - int err = 0; - kstat_named_t *k; - - if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) - return (-1); - - if (topo_prop_set_string(node, pgname, pname, - TOPO_PROP_IMMUTABLE, k->value.str.addr.ptr, &err) == 0) { - return (0); - } else { - whinge(mod, &err, "chip_strprop: failed to add '%s'\n", - pname); - return (-1); - } -} - -static int -add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, - const char *pgname, const char *pname) -{ - int err; - kstat_named_t *k; - - if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) - return (-1); - - if (topo_prop_set_int32(node, pgname, pname, - TOPO_PROP_IMMUTABLE, k->value.l, &err) == 0) { - return (0); - } else { - whinge(mod, &err, "chip_longprop: failed to add '%s'\n", - pname); - return (-1); - } -} - -static int -add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, - const char *pgname, ...) -{ - const char *pname; - va_list ap; - int nerr = 0; - - va_start(ap, pgname); - while ((pname = va_arg(ap, const char *)) != NULL) { - if (add_kstat_longprop(mod, node, ksp, pgname, pname) != 0) - nerr++; /* have whinged elsewhere */ - } - va_end(ap); - - return (nerr == 0 ? 0 : -1); -} - -static int -mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst, - nvlist_t *auth, nvlist_t **nvl) -{ - *nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, - inst, NULL, auth, NULL, NULL, NULL); - return (nvl != NULL ? 0 : -1); /* caller must free nvlist */ -} - -static nvlist_t * -cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask) -{ - int err; - nvlist_t *asru; - - if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) - return (NULL); - - err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION); - err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); - err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid); - err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask); - if (s != NULL) - err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s); - if (err != 0) { - nvlist_free(asru); - (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); - return (NULL); - } - - return (asru); -} - -static nvlist_t * -mem_fmri_create(topo_mod_t *mod) -{ - nvlist_t *asru; - - if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) - return (NULL); - - if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 || - nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) { - nvlist_free(asru); - return (NULL); - } - - return (asru); -} - -static int cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid, chip_t *chip, nvlist_t *auth) { @@ -336,7 +176,9 @@ cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid, * * The core_id in the cpu_info kstat tells us which cpus * share the same core - i.e., are hardware strands of the - * same core. This enumerator does not distinguish stranded + * same core. The core ids do not reset to zero for each + * distinct chip - they number across all cores of all chips. + * This enumerator does not distinguish stranded * cores so core_id is unused. */ if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid], @@ -375,7 +217,7 @@ cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid, TOPO_PROP_IMMUTABLE, cpuid, &err); if (add_kstat_longprops(mod, cnode, chip->chip_cpustats[cpuid], - PGNAME(CPU), CPU_CHIP_ID, CPU_CORE_ID, CPU_CLOG_ID, + PGNAME(CPU), NULL, CPU_CHIP_ID, CPU_CORE_ID, CPU_CLOG_ID, NULL) != 0) nerr++; /* have whinged elsewhere */ } @@ -384,638 +226,6 @@ cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid, } static int -nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node) -{ - int success = 0; - int err; - char *pname = nvpair_name(nvp); - - switch (nvpair_type(nvp)) { - case DATA_TYPE_BOOLEAN_VALUE: { - boolean_t val; - - if (nvpair_value_boolean_value(nvp, &val) == 0 && - topo_prop_set_string(node, pgname, pname, - TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0) - success = 1; - break; - } - - case DATA_TYPE_UINT32: { - uint32_t val; - - if (nvpair_value_uint32(nvp, &val) == 0 && - topo_prop_set_uint32(node, pgname, pname, - TOPO_PROP_IMMUTABLE, val, &err) == 0) - success = 1; - break; - } - - case DATA_TYPE_UINT64: { - uint64_t val; - - if (nvpair_value_uint64(nvp, &val) == 0 && - topo_prop_set_uint64(node, pgname, pname, - TOPO_PROP_IMMUTABLE, val, &err) == 0) - success = 1; - break; - } - - case DATA_TYPE_UINT32_ARRAY: { - uint32_t *arrp; - uint_t nelem; - - if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 && - nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname, - TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0) - success = 1; - break; - } - - case DATA_TYPE_STRING: { - char *str; - - if (nvpair_value_string(nvp, &str) == 0 && - topo_prop_set_string(node, pgname, pname, - TOPO_PROP_IMMUTABLE, str, &err) == 0) - success = 1; - break; - } - - default: - whinge(mod, &err, "nvprop_add: Can't handle type %d for " - "'%s' in property group %s of %s node\n", - nvpair_type(nvp), pname, pgname, topo_node_name(node)); - break; - } - - return (success ? 0 : 1); -} - -static int -chip_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl) -{ - nvpair_t *nvp; - int nerr = 0; - - if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) { - whinge(mod, &nerr, "chip_htconfig: must pass a chip node!"); - return (-1); - } - - for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL; - nvp = nvlist_next_nvpair(htnvl, nvp)) { - if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0) - nerr++; - } - - return (nerr == 0 ? 0 : -1); -} - -static int -dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name, - nvlist_t *auth) -{ - tnode_t *chnode; - nvlist_t *fmri; - char *socket; - int i, nchan; - int err, nerr = 0; - - /* - * We will enumerate the number of channels present even if only - * channel A is in use (i.e., running in 64-bit mode). Only - * the socket 754 package has a single channel. - */ - if (topo_prop_get_string(pnode, PGNAME(MCT), "socket", - &socket, &err) != 0) - return (-1); - - if (strcmp(socket, "Socket 754") == 0) - nchan = 1; - else - nchan = 2; - - topo_mod_strfree(mod, socket); - - if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0) - return (-1); - - for (i = 0; i < nchan; i++) { - if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { - whinge(mod, &nerr, "dramchan_create: mkrsrc " - "failed\n"); - continue; - } - - if ((chnode = topo_node_bind(mod, pnode, name, i, fmri)) - == NULL) { - nvlist_free(fmri); - whinge(mod, &nerr, "dramchan_create: node bind " - "failed\n"); - continue; - } - - nvlist_free(fmri); - - (void) topo_pgroup_create(chnode, &chan_pgroup, &err); - - (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", - TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err); - } - - return (nerr == 0 ? 0 : -1); -} - -static int -cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc, - nvlist_t *auth) -{ - int i, err, nerr = 0; - nvpair_t *nvp; - tnode_t *csnode; - nvlist_t *fmri, **csarr = NULL; - uint64_t csnum; - uint_t ncs; - - if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0) - return (-1); - - if (ncs == 0) - return (0); /* no chip-selects configured on this node */ - - if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0) - return (-1); - - for (i = 0; i < ncs; i++) { - if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) { - whinge(mod, &nerr, "cs_create: cs num property " - "missing\n"); - continue; - } - - if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) { - whinge(mod, &nerr, "cs_create: mkrsrc failed\n"); - continue; - } - - if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri)) - == NULL) { - nvlist_free(fmri); - whinge(mod, &nerr, "cs_create: node bind failed\n"); - continue; - } - - cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */ - - (void) topo_node_asru_set(csnode, fmri, 0, &err); - - (void) topo_pgroup_create(csnode, &cs_pgroup, &err); - - for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL; - nvp = nvlist_next_nvpair(csarr[i], nvp)) { - nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode); - } - } - - return (nerr == 0 ? 0 : -1); -} - -/* - * Registered method for asru computation for rank nodes. The 'node' - * argument identifies the node for which we seek an asru. The 'in' - * argument is used to select which asru we will return, as follows: - * - * - the node name must be "dimm" or "rank" - * - if 'in' is NULL then return any statically defined asru for this node - * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru - * with unum being the hc path to the dimm or rank (this method is called - * as part of dynamic asru computation for rank nodes only, but dimm_create - * also calls it directly to construct a "mem" scheme asru for a dimm node) - * - if 'in' in addition includes an hc-specific member which specifies - * asru-physaddr or asru-offset then these are includes in the "mem" scheme - * asru as additional members physaddr and offset - */ -static int -mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru) -{ - int incl_pa = 0, incl_offset = 0; - nvlist_t *hcsp, *ap; - char *unum, *scheme; - uint64_t pa, offset; - int err; - - if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 || - strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) - return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); - - if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { - if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR, - &pa) == 0) - incl_pa = 1; - - if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET, - &offset) == 0) - incl_offset = 1; - } - - /* use 'fmri' to obtain resource path; could use node resource */ - if (topo_mod_nvl2str(mod, fmri, &unum) < 0) - return (-1); /* mod errno set */ - - if ((ap = mem_fmri_create(mod)) == NULL) { - topo_mod_strfree(mod, unum); - return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); - } - - err = nvlist_add_string(ap, FM_FMRI_MEM_UNUM, unum); - if (incl_pa) - err |= nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa); - if (incl_offset) - err |= nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset); - - topo_mod_strfree(mod, unum); - if (err != 0) { - nvlist_free(ap); - return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); - } - - *asru = ap; - - return (0); -} - -/*ARGSUSED*/ -static int -mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version, - nvlist_t *in, nvlist_t **out) -{ - nvlist_t *asru; - nvlist_t *args, *pargs; - int err; - - if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 && - strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0) - return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); - - if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) - return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); - - if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) { - if (err == ENOENT) { - if (topo_mod_nvdup(mod, args, &asru) < 0) - return (topo_mod_seterrno(mod, EMOD_NOMEM)); - } else { - return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); - } - } else if (mem_asru_create(mod, pargs, &asru) != 0) { - return (-1); /* mod errno already set */ - } - - if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { - nvlist_free(asru); - return (topo_mod_seterrno(mod, EMOD_NOMEM)); - } - - err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU); - err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI); - err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru); - if (err != 0) { - nvlist_free(asru); - nvlist_free(*out); - return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); - } - - nvlist_free(asru); - - return (0); -} - -static int -rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl, nvlist_t *auth) -{ - uint64_t *csnumarr; - char **csnamearr; - uint_t ncs, ncsname; - tnode_t *ranknode; - nvlist_t *fmri, *pfmri = NULL; - uint64_t dsz, rsz; - int nerr = 0; - int err; - int i; - - if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr, - &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames", - &csnamearr, &ncsname) != 0 || ncs != ncsname) { - whinge(mod, &nerr, "rank_create: " - "csnums/csnames extraction failed\n"); - return (nerr); - } - - if (topo_node_resource(pnode, &pfmri, &err) < 0) { - whinge(mod, &nerr, "rank_create: parent fmri lookup " - "failed\n"); - return (nerr); - } - - if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) { - whinge(mod, &nerr, "rank_create: range create failed\n"); - nvlist_free(pfmri); - return (nerr); - } - - if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz, - &err) == 0) { - rsz = dsz / ncs; - } else { - whinge(mod, &nerr, "rank_create: parent dimm has no size\n"); - return (nerr); - } - - for (i = 0; i < ncs; i++) { - if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) { - whinge(mod, &nerr, "rank_create: mkrsrc failed\n"); - continue; - } - - if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i, - fmri)) == NULL) { - nvlist_free(fmri); - whinge(mod, &nerr, "rank_create: node bind " - "failed\n"); - continue; - } - - nvlist_free(fmri); - - (void) topo_node_fru_set(ranknode, pfmri, 0, &err); - - /* - * If a rank is faulted the asru is the associated - * chip-select, but if a page within a rank is faulted - * the asru is just that page. Hence the dual preconstructed - * and computed ASRU. - */ - if (topo_method_register(mod, ranknode, rank_methods) < 0) - whinge(mod, &nerr, "rank_create: " - "topo_method_register failed"); - - (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]], - TOPO_ASRU_COMPUTE, &err); - - (void) topo_pgroup_create(ranknode, &rank_pgroup, &err); - - (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size", - TOPO_PROP_IMMUTABLE, rsz, &err); - - (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname", - TOPO_PROP_IMMUTABLE, csnamearr[i], &err); - - (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum", - TOPO_PROP_IMMUTABLE, csnumarr[i], &err); - } - - nvlist_free(pfmri); - - return (nerr); -} - -static int -dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc, - nvlist_t *auth) -{ - int i, err, nerr = 0; - nvpair_t *nvp; - tnode_t *dimmnode; - nvlist_t *fmri, *asru, **dimmarr = NULL; - uint64_t num; - uint_t ndimm; - - if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) { - whinge(mod, NULL, "dimm_create: dimmlist lookup failed\n"); - return (-1); - } - - if (ndimm == 0) - return (0); /* no dimms present on this node */ - - if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) { - whinge(mod, NULL, "dimm_create: range create failed\n"); - return (-1); - } - - for (i = 0; i < ndimm; i++) { - if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) { - whinge(mod, &nerr, "dimm_create: dimm num property " - "missing\n"); - continue; - } - - if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) { - whinge(mod, &nerr, "dimm_create: mkrsrc failed\n"); - continue; - } - - if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri)) - == NULL) { - nvlist_free(fmri); - whinge(mod, &nerr, "dimm_create: node bind " - "failed\n"); - continue; - } - - if (topo_method_register(mod, dimmnode, dimm_methods) < 0) - whinge(mod, &nerr, "dimm_create: " - "topo_method_register failed"); - - /* - * Use the mem computation method directly to publish the asru - * in the "mem" scheme. - */ - if (mem_asru_create(mod, fmri, &asru) == 0) { - (void) topo_node_asru_set(dimmnode, asru, 0, &err); - nvlist_free(asru); - } else { - - nvlist_free(fmri); - whinge(mod, &nerr, "dimm_create: mem_asru_compute " - "failed\n"); - continue; - } - - (void) topo_node_fru_set(dimmnode, fmri, 0, &err); - - nvlist_free(fmri); - - (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err); - - for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL; - nvp = nvlist_next_nvpair(dimmarr[i], nvp)) { - if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY && - strcmp(nvpair_name(nvp), "csnums") == 0 || - nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY && - strcmp(nvpair_name(nvp), "csnames") == 0) - continue; /* used in rank_create() */ - - nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode); - } - - nerr += rank_create(mod, dimmnode, dimmarr[i], auth); - } - - return (nerr == 0 ? 0 : -1); -} - -static nvlist_t * -mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id) -{ - mc_snapshot_info_t mcs; - void *buf = NULL; - uint8_t ver; - - nvlist_t *nvl = NULL; - char path[64]; - int fd, err; - - (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); - fd = open(path, O_RDONLY); - - if (fd == -1) { - /* - * Some v20z and v40z systems may have had the 3rd-party - * NWSnps packagae installed which installs a /dev/mc - * link. So try again via /devices. - */ - (void) snprintf(path, sizeof (path), - "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd", - MC_AMD_DEV_OFFSET + id); - fd = open(path, O_RDONLY); - } - - if (fd == -1) { - whinge(mod, NULL, "mc failed to open %s: %s\n", - path, strerror(errno)); - return (NULL); - } - - if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || - (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || - ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) { - - whinge(mod, NULL, "mc failed to snapshot %s: %s\n", - path, strerror(errno)); - - free(buf); - (void) close(fd); - return (NULL); - } - - (void) close(fd); - err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); - topo_mod_free(mod, buf, mcs.mcs_size); - - - if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) { - whinge(mod, NULL, "mc nvlist is not versioned\n"); - nvlist_free(nvl); - return (NULL); - } else if (ver != MC_NVLIST_VERS1) { - whinge(mod, NULL, "mc nvlist version mismatch\n"); - nvlist_free(nvl); - return (NULL); - } - - return (err ? NULL : nvl); -} - -static int -mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth) -{ - int err, rc = 0; - tnode_t *mcnode; - nvlist_t *fmri; - nvpair_t *nvp; - nvlist_t *mc = NULL; - int i; - - if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) { - whinge(mod, NULL, "mc_create: mkrsrc failed\n"); - return (-1); - } - - if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) { - nvlist_free(fmri); - whinge(mod, NULL, "mc_create: node range create failed\n"); - return (-1); - } - - /* - * Gather and create memory controller topology - */ - if ((mc = mc_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL || - (mcnode = topo_node_bind(mod, pnode, - name, 0, fmri)) == NULL) { - if (mc != NULL) - nvlist_free(mc); - topo_node_range_destroy(pnode, name); - nvlist_free(fmri); - whinge(mod, NULL, "mc_create: mc lookup or bind failed\n"); - return (-1); - } - - (void) topo_node_fru_set(mcnode, NULL, 0, &err); - nvlist_free(fmri); - - /* - * Add memory controller properties - */ - (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); - - for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL; - nvp = nvlist_next_nvpair(mc, nvp)) { - char *name = nvpair_name(nvp); - data_type_t type = nvpair_type(nvp); - - if (type == DATA_TYPE_NVLIST_ARRAY && - (strcmp(name, "cslist") == 0 || - strcmp(name, "dimmlist") == 0)) { - continue; - } else if (type == DATA_TYPE_UINT8 && - strcmp(name, MC_NVLIST_VERSTR) == 0) { - continue; - } else if (type == DATA_TYPE_NVLIST && - strcmp(name, "htconfig") == 0) { - nvlist_t *htnvl; - - (void) nvpair_value_nvlist(nvp, &htnvl); - if (chip_htconfig(mod, pnode, htnvl) != 0) - rc = -1; - } else { - if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0) - rc = -1; - } - } - - if (dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 || - cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 || - dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0) - rc = -1; - - /* - * Free the fmris for the chip-selects allocated in cs_create - */ - for (i = 0; i < MC_CHIP_NCS; i++) { - if (cs_fmri[i] != NULL) { - nvlist_free(cs_fmri[i]); - cs_fmri[i] = NULL; - } - } - - nvlist_free(mc); - return (rc); -} - -static int chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, topo_instance_t min, topo_instance_t max, chip_t *chip, nvlist_t *auth) { @@ -1043,6 +253,8 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, } for (i = 0; i <= chip->chip_ncpustats; i++) { + const char *vendor; + int32_t fms[3]; kstat_named_t *k; int err, chipid; @@ -1086,16 +298,24 @@ chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, (void) topo_pgroup_create(cnode, &chip_pgroup, &err); if (add_kstat_strprop(mod, cnode, ksp, PGNAME(CHIP), - CHIP_VENDOR_ID) != 0) + CHIP_VENDOR_ID, &vendor) != 0) nerr++; /* have whinged elsewhere */ if (add_kstat_longprops(mod, cnode, ksp, PGNAME(CHIP), - CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL) != 0) + fms, CHIP_FAMILY, CHIP_MODEL, CHIP_STEPPING, NULL) != 0) nerr++; /* have whinged elsewhere */ if (cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip, auth) - != 0 || mc_create(mod, cnode, MCT_NODE_NAME, auth) != 0) + != 0) nerr++; /* have whinged elsewhere */ + + /* + * Create memory-controller node under a chip for architectures + * that may have on-chip memory-controller(s). + */ + if (strcmp(vendor, "AuthenticAMD") == 0) + amd_mc_create(mod, cnode, MCT_NODE_NAME, auth, + fms[0], fms[1], fms[2], &nerr); } topo_mod_free(mod, chipmap, BT_BITOUL(max) * sizeof (ulong_t)); @@ -1116,12 +336,17 @@ chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, int rv = 0; chip_t *chip = (chip_t *)arg; nvlist_t *auth = NULL; + int intel_mc; auth = topo_mod_auth(mod, pnode); + intel_mc = mc_offchip_open(); if (strcmp(name, CHIP_NODE_NAME) == 0) rv = chip_create(mod, pnode, name, min, max, chip, auth); + if (intel_mc) + (void) mc_offchip_create(mod, pnode, "memory-controller", auth); + nvlist_free(auth); return (rv); diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h index 4bc3f23ded..3f36797378 100644 --- a/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip.h @@ -31,6 +31,7 @@ #include <kstat.h> #include <libnvpair.h> #include <fm/libtopo.h> +#include <fm/topo_mod.h> #ifdef __cplusplus extern "C" { @@ -93,6 +94,37 @@ extern int simple_chip_label(topo_mod_t *, tnode_t *, topo_version_t, extern int g4_chip_label(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); +/* + * Support functions of chip_subr.c + */ +extern void whinge(topo_mod_t *, int *, const char *, ...); +extern int nvprop_add(topo_mod_t *, nvpair_t *, const char *, tnode_t *); +extern int add_kstat_strprop(topo_mod_t *, tnode_t *, kstat_t *, + const char *, const char *, const char **); +extern int add_kstat_longprop(topo_mod_t *, tnode_t *, kstat_t *, + const char *, const char *, int32_t *); +extern int add_kstat_longprops(topo_mod_t *, tnode_t *, kstat_t *, + const char *, int32_t *, ...); +extern int mkrsrc(topo_mod_t *, tnode_t *, const char *, int, + nvlist_t *, nvlist_t **); +extern nvlist_t *cpu_fmri_create(topo_mod_t *, uint32_t, char *, uint8_t); +extern nvlist_t *mem_fmri_create(topo_mod_t *, const char *); +extern int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t, + nvlist_t *, nvlist_t **); +extern int mem_asru_create(topo_mod_t *, nvlist_t *, nvlist_t **); + +/* + * Prototypes for chip_amd.c + */ +extern void amd_mc_create(topo_mod_t *, tnode_t *, const char *, nvlist_t *, + int, int, int, int *); + +/* + * Prototypes for chip_intel.c + */ +extern int mc_offchip_open(void); +extern int mc_offchip_create(topo_mod_t *, tnode_t *, const char *, nvlist_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_amd.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_amd.c new file mode 100644 index 0000000000..d7e7f54eee --- /dev/null +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_amd.c @@ -0,0 +1,659 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * AMD memory enumeration + */ + +#include <sys/types.h> +#include <unistd.h> +#include <stropts.h> +#include <sys/fm/protocol.h> +#include <sys/mc.h> +#include <sys/mc_amd.h> +#include <fm/topo_mod.h> +#include <strings.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "chip.h" + +#define MAX_CHANNUM 1 +#define MAX_DIMMNUM 7 +#define MAX_CSNUM 7 + +static const topo_pgroup_info_t cs_pgroup = + { PGNAME(CS), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t dimm_pgroup = + { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t mc_pgroup = + { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t rank_pgroup = + { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t chan_pgroup = + { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; + +static const topo_method_t dimm_methods[] = { + { SIMPLE_DIMM_LBL, "Property method", 0, + TOPO_STABILITY_INTERNAL, simple_dimm_label}, + { SIMPLE_DIMM_LBL_MP, "Property method", 0, + TOPO_STABILITY_INTERNAL, simple_dimm_label_mp}, + { SEQ_DIMM_LBL, "Property method", 0, + TOPO_STABILITY_INTERNAL, seq_dimm_label}, + { NULL } +}; + +static const topo_method_t rank_methods[] = { + { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, + TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, + mem_asru_compute }, + { NULL } +}; + +static const topo_method_t gen_cs_methods[] = { + { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, + TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, + mem_asru_compute }, + { NULL } +}; + +static nvlist_t *cs_fmri[MC_CHIP_NCS]; + +/* + * Called when there is no memory-controller driver to provide topology + * information. Generate a maximal memory topology that is appropriate + * for the chip revision. The memory-controller node has already been + * bound as mcnode, and the parent of that is cnode. + * + * We create a tree of dram-channel and chip-select nodes below the + * memory-controller node. There will be two dram channels and 8 chip-selects + * below each, regardless of actual socket type, processor revision and so on. + * This is adequate for generic diagnosis up to family 0x10 revision C. + * When support for revision D is implemented (or maybe C) we should take + * the opportunity to rework the topology tree completely (socket change will + * mean there can be no diagnosis history tied to the topology). + */ +/*ARGSUSED*/ +static int +amd_generic_mc_create(topo_mod_t *mod, tnode_t *cnode, tnode_t *mcnode, + int family, int model, int stepping, nvlist_t *auth) +{ + int chan, cs; + + /* + * Elsewhere we have already returned for families less than 0xf. + * This "generic" topology is adequate for all of family 0xf and + * for revisions A, B and C of family 0x10 (A = model 0, B = model 1, + * we'll guess C = model 3 at this point). + */ + if (family > 0x10 || (family == 0x10 && model > 3)) + return (1); + + if (topo_node_range_create(mod, mcnode, CHAN_NODE_NAME, 0, + MAX_CHANNUM) < 0) { + whinge(mod, NULL, "amd_generic_mc_create: range create for " + "channels failed\n"); + return (-1); + } + + for (chan = 0; chan <= MAX_CHANNUM; chan++) { + tnode_t *chnode; + nvlist_t *fmri; + int err; + + if (mkrsrc(mod, mcnode, CHAN_NODE_NAME, chan, auth, + &fmri) != 0) { + whinge(mod, NULL, "amd_generic_mc_create: mkrsrc " + "failed\n"); + return (-1); + } + + if ((chnode = topo_node_bind(mod, mcnode, CHAN_NODE_NAME, + chan, fmri)) == NULL) { + nvlist_free(fmri); + whinge(mod, NULL, "amd_generic_mc_create: node " + "bind failed\n"); + return (-1); + } + + nvlist_free(fmri); + + (void) topo_pgroup_create(chnode, &chan_pgroup, &err); + + (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", + TOPO_PROP_IMMUTABLE, chan == 0 ? "A" : "B", &err); + + if (topo_node_range_create(mod, chnode, CS_NODE_NAME, + 0, MAX_CSNUM) < 0) { + whinge(mod, NULL, "amd_generic_mc_create: " + "range create for cs failed\n"); + return (-1); + } + + for (cs = 0; cs <= MAX_CSNUM; cs++) { + tnode_t *csnode; + + if (mkrsrc(mod, chnode, CS_NODE_NAME, cs, auth, + &fmri) != 0) { + whinge(mod, NULL, "amd_generic_mc_create: " + "mkrsrc for cs failed\n"); + return (-1); + } + + if ((csnode = topo_node_bind(mod, chnode, CS_NODE_NAME, + cs, fmri)) == NULL) { + nvlist_free(fmri); + whinge(mod, NULL, "amd_generic_mc_create: " + "bind for cs failed\n"); + return (-1); + } + + /* + * Dynamic ASRU for page faults within a chip-select. + * The topology does not represent pages (there are + * too many) so when a page is faulted we generate + * an ASRU to represent the individual page. + */ + if (topo_method_register(mod, csnode, + gen_cs_methods) < 0) + whinge(mod, NULL, "amd_generic_mc_create: " + "method registration failed\n"); + + (void) topo_node_asru_set(csnode, fmri, + TOPO_ASRU_COMPUTE, &err); + + nvlist_free(fmri); + } + } + + return (0); +} + +static nvlist_t * +amd_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id) +{ + mc_snapshot_info_t mcs; + void *buf = NULL; + uint8_t ver; + + nvlist_t *nvl = NULL; + char path[64]; + int fd, err; + + (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); + fd = open(path, O_RDONLY); + + if (fd == -1) { + /* + * Some v20z and v40z systems may have had the 3rd-party + * NWSnps packagae installed which installs a /dev/mc + * link. So try again via /devices. + */ + (void) snprintf(path, sizeof (path), + "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd", + MC_AMD_DEV_OFFSET + id); + fd = open(path, O_RDONLY); + } + + if (fd == -1) + return (NULL); /* do not whinge */ + + if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || + (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || + ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) { + + whinge(mod, NULL, "mc failed to snapshot %s: %s\n", + path, strerror(errno)); + + free(buf); + (void) close(fd); + return (NULL); + } + + (void) close(fd); + err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); + topo_mod_free(mod, buf, mcs.mcs_size); + + + if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) { + whinge(mod, NULL, "mc nvlist is not versioned\n"); + nvlist_free(nvl); + return (NULL); + } else if (ver != MC_NVLIST_VERS1) { + whinge(mod, NULL, "mc nvlist version mismatch\n"); + nvlist_free(nvl); + return (NULL); + } + + return (err ? NULL : nvl); +} + +int +amd_rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl, + nvlist_t *auth) +{ + uint64_t *csnumarr; + char **csnamearr; + uint_t ncs, ncsname; + tnode_t *ranknode; + nvlist_t *fmri, *pfmri = NULL; + uint64_t dsz, rsz; + int nerr = 0; + int err; + int i; + + if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr, + &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames", + &csnamearr, &ncsname) != 0 || ncs != ncsname) { + whinge(mod, &nerr, "amd_rank_create: " + "csnums/csnames extraction failed\n"); + return (nerr); + } + + if (topo_node_resource(pnode, &pfmri, &err) < 0) { + whinge(mod, &nerr, "amd_rank_create: parent fmri lookup " + "failed\n"); + return (nerr); + } + + if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) { + whinge(mod, &nerr, "amd_rank_create: range create failed\n"); + nvlist_free(pfmri); + return (nerr); + } + + if (topo_prop_get_uint64(pnode, PGNAME(DIMM), "size", &dsz, + &err) == 0) { + rsz = dsz / ncs; + } else { + whinge(mod, &nerr, "amd_rank_create: parent dimm has no " + "size\n"); + return (nerr); + } + + for (i = 0; i < ncs; i++) { + if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, auth, &fmri) < 0) { + whinge(mod, &nerr, "amd_rank_create: mkrsrc failed\n"); + continue; + } + + if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i, + fmri)) == NULL) { + nvlist_free(fmri); + whinge(mod, &nerr, "amd_rank_create: node bind " + "failed\n"); + continue; + } + + nvlist_free(fmri); + + (void) topo_node_fru_set(ranknode, pfmri, 0, &err); + + /* + * If a rank is faulted the asru is the associated + * chip-select, but if a page within a rank is faulted + * the asru is just that page. Hence the dual preconstructed + * and computed ASRU. + */ + if (topo_method_register(mod, ranknode, rank_methods) < 0) + whinge(mod, &nerr, "amd_rank_create: " + "topo_method_register failed"); + + (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]], + TOPO_ASRU_COMPUTE, &err); + + (void) topo_pgroup_create(ranknode, &rank_pgroup, &err); + + (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "size", + TOPO_PROP_IMMUTABLE, rsz, &err); + + (void) topo_prop_set_string(ranknode, PGNAME(RANK), "csname", + TOPO_PROP_IMMUTABLE, csnamearr[i], &err); + + (void) topo_prop_set_uint64(ranknode, PGNAME(RANK), "csnum", + TOPO_PROP_IMMUTABLE, csnumarr[i], &err); + } + + nvlist_free(pfmri); + + return (nerr); +} + +static int +amd_dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, + nvlist_t *mc, nvlist_t *auth) +{ + int i, err, nerr = 0; + nvpair_t *nvp; + tnode_t *dimmnode; + nvlist_t *fmri, *asru, **dimmarr = NULL; + uint64_t num; + uint_t ndimm; + + if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) { + whinge(mod, NULL, "amd_dimm_create: dimmlist lookup failed\n"); + return (-1); + } + + if (ndimm == 0) + return (0); /* no dimms present on this node */ + + if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) { + whinge(mod, NULL, "amd_dimm_create: range create failed\n"); + return (-1); + } + + for (i = 0; i < ndimm; i++) { + if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) { + whinge(mod, &nerr, "amd_dimm_create: dimm num property " + "missing\n"); + continue; + } + + if (mkrsrc(mod, pnode, name, num, auth, &fmri) < 0) { + whinge(mod, &nerr, "amd_dimm_create: mkrsrc failed\n"); + continue; + } + + if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri)) + == NULL) { + nvlist_free(fmri); + whinge(mod, &nerr, "amd_dimm_create: node bind " + "failed\n"); + continue; + } + + if (topo_method_register(mod, dimmnode, dimm_methods) < 0) + whinge(mod, &nerr, "dimm_create: " + "topo_method_register failed"); + + /* + * Use the mem computation method directly to publish the asru + * in the "mem" scheme. + */ + if (mem_asru_create(mod, fmri, &asru) == 0) { + (void) topo_node_asru_set(dimmnode, asru, 0, &err); + nvlist_free(asru); + } else { + + nvlist_free(fmri); + whinge(mod, &nerr, "amd_dimm_create: " + "mem_asru_create failed\n"); + continue; + } + + (void) topo_node_fru_set(dimmnode, fmri, 0, &err); + + nvlist_free(fmri); + + (void) topo_pgroup_create(dimmnode, &dimm_pgroup, &err); + + for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL; + nvp = nvlist_next_nvpair(dimmarr[i], nvp)) { + if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY && + strcmp(nvpair_name(nvp), "csnums") == 0 || + nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY && + strcmp(nvpair_name(nvp), "csnames") == 0) + continue; /* used in amd_rank_create() */ + + nerr += nvprop_add(mod, nvp, PGNAME(DIMM), dimmnode); + } + + nerr += amd_rank_create(mod, dimmnode, dimmarr[i], auth); + } + + return (nerr == 0 ? 0 : -1); +} + +static int +amd_cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc, + nvlist_t *auth) +{ + int i, err, nerr = 0; + nvpair_t *nvp; + tnode_t *csnode; + nvlist_t *fmri, **csarr = NULL; + uint64_t csnum; + uint_t ncs; + + if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0) + return (-1); + + if (ncs == 0) + return (0); /* no chip-selects configured on this node */ + + if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0) + return (-1); + + for (i = 0; i < ncs; i++) { + if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) { + whinge(mod, &nerr, "amd_cs_create: cs num property " + "missing\n"); + continue; + } + + if (mkrsrc(mod, pnode, name, csnum, auth, &fmri) != 0) { + whinge(mod, &nerr, "amd_cs_create: mkrsrc failed\n"); + continue; + } + + if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri)) + == NULL) { + nvlist_free(fmri); + whinge(mod, &nerr, "amd_cs_create: node bind failed\n"); + continue; + } + + cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */ + + (void) topo_node_asru_set(csnode, fmri, 0, &err); + + (void) topo_pgroup_create(csnode, &cs_pgroup, &err); + + for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL; + nvp = nvlist_next_nvpair(csarr[i], nvp)) { + nerr += nvprop_add(mod, nvp, PGNAME(CS), csnode); + } + } + + return (nerr == 0 ? 0 : -1); +} + +static int +amd_dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name, + nvlist_t *auth) +{ + tnode_t *chnode; + nvlist_t *fmri; + char *socket; + int i, nchan; + int err, nerr = 0; + + /* + * We will enumerate the number of channels present even if only + * channel A is in use (i.e., running in 64-bit mode). Only + * the socket 754 package has a single channel. + */ + if (topo_prop_get_string(pnode, PGNAME(MCT), "socket", + &socket, &err) == 0 && strcmp(socket, "Socket 754") == 0) + nchan = 1; + else + nchan = 2; + + topo_mod_strfree(mod, socket); + + if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0) + return (-1); + + for (i = 0; i < nchan; i++) { + if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { + whinge(mod, &nerr, "amd_dramchan_create: mkrsrc " + "failed\n"); + continue; + } + + if ((chnode = topo_node_bind(mod, pnode, name, i, fmri)) + == NULL) { + nvlist_free(fmri); + whinge(mod, &nerr, "amd_dramchan_create: node bind " + "failed\n"); + continue; + } + + nvlist_free(fmri); + + (void) topo_pgroup_create(chnode, &chan_pgroup, &err); + + (void) topo_prop_set_string(chnode, PGNAME(CHAN), "channel", + TOPO_PROP_IMMUTABLE, i == 0 ? "A" : "B", &err); + } + + return (nerr == 0 ? 0 : -1); +} + +static int +amd_htconfig(topo_mod_t *mod, tnode_t *cnode, nvlist_t *htnvl) +{ + nvpair_t *nvp; + int nerr = 0; + + if (strcmp(topo_node_name(cnode), CHIP_NODE_NAME) != 0) { + whinge(mod, &nerr, "amd_htconfig: must pass a chip node!"); + return (-1); + } + + for (nvp = nvlist_next_nvpair(htnvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(htnvl, nvp)) { + if (nvprop_add(mod, nvp, PGNAME(CHIP), cnode) != 0) + nerr++; + } + + return (nerr == 0 ? 0 : -1); +} + +void +amd_mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth, + int family, int model, int stepping, int *nerrp) +{ + tnode_t *mcnode; + nvlist_t *fmri; + nvpair_t *nvp; + nvlist_t *mc = NULL; + int i; + + /* + * Return with no error for anything before AMD family 0xf - we + * won't generate even a generic memory topolofy for earlier + * families. + */ + if (family < 0xf) + return; + + if (mkrsrc(mod, pnode, name, 0, auth, &fmri) != 0) { + whinge(mod, nerrp, "mc_create: mkrsrc failed\n"); + return; + } + + if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) { + nvlist_free(fmri); + whinge(mod, nerrp, "mc_create: node range create failed\n"); + return; + } + + if ((mcnode = topo_node_bind(mod, pnode, name, 0, + fmri)) == NULL) { + nvlist_free(mc); + topo_node_range_destroy(pnode, name); + nvlist_free(fmri); + whinge(mod, nerrp, "mc_create: mc bind failed\n"); + return; + } + (void) topo_node_fru_set(mcnode, NULL, 0, nerrp); + nvlist_free(fmri); + + if ((mc = amd_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL) { + /* + * If a memory-controller driver exists for this chip model + * it has not attached or has otherwise malfunctioned; + * alternatively no memory-controller driver exists for this + * (presumably newly-released) cpu model. We fallback to + * creating a generic maximal topology. + */ + if (amd_generic_mc_create(mod, pnode, mcnode, + family, model, stepping, auth) != 0) + ++*nerrp; + return; + } + + /* + * Add memory controller properties + */ + (void) topo_pgroup_create(mcnode, &mc_pgroup, nerrp); + + for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(mc, nvp)) { + char *name = nvpair_name(nvp); + data_type_t type = nvpair_type(nvp); + + if (type == DATA_TYPE_NVLIST_ARRAY && + (strcmp(name, "cslist") == 0 || + strcmp(name, "dimmlist") == 0)) { + continue; + } else if (type == DATA_TYPE_UINT8 && + strcmp(name, MC_NVLIST_VERSTR) == 0) { + continue; + } else if (type == DATA_TYPE_NVLIST && + strcmp(name, "htconfig") == 0) { + nvlist_t *htnvl; + + (void) nvpair_value_nvlist(nvp, &htnvl); + if (amd_htconfig(mod, pnode, htnvl) != 0) + ++*nerrp; + } else { + if (nvprop_add(mod, nvp, PGNAME(MCT), mcnode) != 0) + ++*nerrp; + } + } + + if (amd_dramchan_create(mod, mcnode, CHAN_NODE_NAME, auth) != 0 || + amd_cs_create(mod, mcnode, CS_NODE_NAME, mc, auth) != 0 || + amd_dimm_create(mod, mcnode, DIMM_NODE_NAME, mc, auth) != 0) + ++*nerrp; + + /* + * Free the fmris for the chip-selects allocated in amd_cs_create + */ + for (i = 0; i < MC_CHIP_NCS; i++) { + if (cs_fmri[i] != NULL) { + nvlist_free(cs_fmri[i]); + cs_fmri[i] = NULL; + } + } + + nvlist_free(mc); +} diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c new file mode 100644 index 0000000000..c64c576df1 --- /dev/null +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_intel.c @@ -0,0 +1,357 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <strings.h> +#include <limits.h> +#include <alloca.h> +#include <kstat.h> +#include <fcntl.h> +#include <errno.h> +#include <libnvpair.h> +#include <sys/types.h> +#include <sys/bitmap.h> +#include <sys/processor.h> +#include <sys/param.h> +#include <sys/fm/protocol.h> +#include <sys/systeminfo.h> +#include <sys/mc.h> +#include <sys/mc_amd.h> +#include <sys/mc_intel.h> +#include <fm/topo_mod.h> + +#include "chip.h" + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +static const topo_pgroup_info_t dimm_channel_pgroup = + { PGNAME(CHAN), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t dimm_pgroup = + { PGNAME(DIMM), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t rank_pgroup = + { PGNAME(RANK), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_pgroup_info_t mc_pgroup = + { PGNAME(MCT), TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 }; +static const topo_method_t rank_methods[] = { + { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, + TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, + mem_asru_compute }, + { NULL } +}; + +static const topo_method_t dimm_methods[] = { + { SIMPLE_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, + simple_dimm_label}, + { SIMPLE_DIMM_LBL_MP, "Property method", 0, TOPO_STABILITY_INTERNAL, + simple_dimm_label_mp}, + { SEQ_DIMM_LBL, "Property method", 0, TOPO_STABILITY_INTERNAL, + seq_dimm_label}, + { NULL } +}; + +static int mc_fd; + +int +mc_offchip_open() +{ + mc_fd = open("/dev/mc/mc", O_RDONLY); + return (mc_fd != -1); +} + +void +mc_add_ranks(topo_mod_t *mod, tnode_t *dnode, nvlist_t *auth, int dimm, + nvlist_t **ranks_nvp, int nranks, char *serial, char *part, char *rev) +{ + int i; + int rank; + tnode_t *rnode; + nvpair_t *nvp; + nvlist_t *fmri; + int err = 0; + + rank = dimm * 2; + if (topo_node_range_create(mod, dnode, RANK, rank, + rank + nranks - 1) < 0) { + whinge(mod, NULL, "mc_add_dimms: node range create failed" + " for rank\n"); + return; + } + for (i = 0; i < nranks; i++) { + fmri = topo_mod_hcfmri(mod, dnode, FM_HC_SCHEME_VERSION, + RANK, rank, NULL, auth, part, rev, serial); + if (fmri == NULL) { + whinge(mod, NULL, + "mc_add_ranks: topo_mod_hcfmri failed\n"); + return; + } + if ((rnode = topo_node_bind(mod, dnode, RANK, rank, + fmri)) == NULL) { + nvlist_free(fmri); + whinge(mod, NULL, "mc_add_ranks: node bind failed" + " for ranks\n"); + return; + } + (void) topo_node_fru_set(rnode, NULL, 0, &err); + + if (topo_method_register(mod, rnode, rank_methods) < 0) + whinge(mod, &err, "rank_create: " + "topo_method_register failed"); + + (void) topo_node_asru_set(rnode, fmri, TOPO_ASRU_COMPUTE, &err); + + nvlist_free(fmri); + + (void) topo_pgroup_create(rnode, &rank_pgroup, &err); + for (nvp = nvlist_next_nvpair(ranks_nvp[i], NULL); nvp != NULL; + nvp = nvlist_next_nvpair(ranks_nvp[i], nvp)) { + (void) nvprop_add(mod, nvp, PGNAME(RANK), rnode); + } + rank++; + } +} + +static void +mc_add_dimms(topo_mod_t *mod, tnode_t *pnode, nvlist_t *auth, + nvlist_t **nvl, uint_t ndimms) +{ + int i; + nvlist_t *fmri; + tnode_t *dnode; + nvpair_t *nvp; + int err; + nvlist_t **ranks_nvp; + uint_t nranks = 0; + char *serial = NULL; + char *part = NULL; + char *rev = NULL; + char *label = NULL; + char *name; + + if (topo_node_range_create(mod, pnode, DIMM, 0, ndimms-1) < 0) { + whinge(mod, NULL, + "mc_add_dimms: node range create failed\n"); + return; + } + for (i = 0; i < ndimms; i++) { + for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; + nvp = nvlist_next_nvpair(nvl[i], nvp)) { + name = nvpair_name(nvp); + if (strcmp(name, MCINTEL_NVLIST_RANKS) == 0) { + (void) nvpair_value_nvlist_array(nvp, + &ranks_nvp, &nranks); + } else if (strcmp(name, FM_FMRI_HC_SERIAL_ID) == 0) { + (void) nvpair_value_string(nvp, &serial); + } else if (strcmp(name, FM_FMRI_HC_PART) == 0) { + (void) nvpair_value_string(nvp, &part); + } else if (strcmp(name, FM_FMRI_HC_REVISION) == 0) { + (void) nvpair_value_string(nvp, &rev); + } else if (strcmp(name, FM_FAULT_FRU_LABEL) == 0) { + (void) nvpair_value_string(nvp, &label); + } + } + fmri = NULL; + fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, + DIMM, i, NULL, auth, part, rev, serial); + if (fmri == NULL) { + whinge(mod, NULL, + "mc_add_dimms: topo_mod_hcfmri failed\n"); + return; + } + if ((dnode = topo_node_bind(mod, pnode, DIMM, i, + fmri)) == NULL) { + nvlist_free(fmri); + whinge(mod, NULL, "mc_add_dimms: node bind failed" + " for dimm\n"); + return; + } + + if (topo_method_register(mod, dnode, dimm_methods) < 0) + whinge(mod, NULL, "mc_add_dimms: " + "topo_method_register failed"); + + (void) topo_node_fru_set(dnode, fmri, 0, &err); + nvlist_free(fmri); + (void) topo_pgroup_create(dnode, &dimm_pgroup, &err); + + for (nvp = nvlist_next_nvpair(nvl[i], NULL); nvp != NULL; + nvp = nvlist_next_nvpair(nvl[i], nvp)) { + name = nvpair_name(nvp); + if (strcmp(name, MCINTEL_NVLIST_RANKS) != 0 && + strcmp(name, FM_FAULT_FRU_LABEL) != 0) { + (void) nvprop_add(mod, nvp, PGNAME(DIMM), + dnode); + } + } + if (label) + (void) topo_node_label_set(dnode, label, &err); + + if (nranks) { + mc_add_ranks(mod, dnode, auth, i, ranks_nvp, nranks, + serial, part, rev); + } + } +} + +static int +mc_add_channel(topo_mod_t *mod, tnode_t *pnode, int channel, nvlist_t *auth, + nvlist_t *nvl) +{ + tnode_t *mc_channel; + nvlist_t *fmri; + nvlist_t **dimm_nvl; + uint_t ndimms; + int err; + + if (mkrsrc(mod, pnode, DRAMCHANNEL, channel, auth, &fmri) != 0) { + whinge(mod, NULL, "mc_add_channel: mkrsrc failed\n"); + return (-1); + } + if ((mc_channel = topo_node_bind(mod, pnode, DRAMCHANNEL, channel, + fmri)) == NULL) { + whinge(mod, NULL, "mc_add_channel: node bind failed for %s\n", + DRAMCHANNEL); + nvlist_free(fmri); + return (-1); + } + (void) topo_node_fru_set(mc_channel, NULL, 0, &err); + nvlist_free(fmri); + (void) topo_pgroup_create(mc_channel, &dimm_channel_pgroup, &err); + if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_DIMMS, &dimm_nvl, + &ndimms) == 0) { + mc_add_dimms(mod, mc_channel, auth, dimm_nvl, ndimms); + } + return (0); +} + +static int +mc_nb_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *auth, + nvlist_t *nvl) +{ + int err; + int i, j; + int channel; + int nmc; + tnode_t *mcnode; + nvlist_t *fmri; + nvlist_t **channel_nvl; + uint_t nchannels; + + if (nvlist_lookup_nvlist_array(nvl, MCINTEL_NVLIST_MC, &channel_nvl, + &nchannels) != 0) { + whinge(mod, NULL, + "mc_nb_create: failed to find channel information\n"); + return (-1); + } + nmc = nchannels / 2; + if (topo_node_range_create(mod, pnode, name, 0, nmc-1) < 0) { + whinge(mod, NULL, + "mc_nb_create: node range create failed\n"); + return (-1); + } + channel = 0; + for (i = 0; i < nmc; i++) { + if (mkrsrc(mod, pnode, name, i, auth, &fmri) != 0) { + whinge(mod, NULL, "mc_nb_create: mkrsrc failed\n"); + return (-1); + } + if ((mcnode = topo_node_bind(mod, pnode, name, i, + fmri)) == NULL) { + whinge(mod, NULL, "chip_create: node bind failed" + " for memory-controller\n"); + nvlist_free(fmri); + return (-1); + } + + (void) topo_node_fru_set(mcnode, NULL, 0, &err); + nvlist_free(fmri); + (void) topo_pgroup_create(mcnode, &mc_pgroup, &err); + + if (topo_node_range_create(mod, mcnode, DRAMCHANNEL, channel, + channel + 1) < 0) { + whinge(mod, NULL, + "mc_nb_create: channel node range create failed\n"); + return (-1); + } + for (j = 0; j < 2; j++) { + if (mc_add_channel(mod, mcnode, channel, auth, + channel_nvl[channel]) < 0) { + return (-1); + } + channel++; + } + } + + return (NULL); +} + +int +mc_offchip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, + nvlist_t *auth) +{ + mc_snapshot_info_t mcs; + void *buf = NULL; + nvlist_t *nvl; + uint8_t ver; + int rc; + + if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || + (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || + ioctl(mc_fd, MC_IOC_SNAPSHOT, buf) == -1) { + + whinge(mod, NULL, "mc failed to snapshot %s\n", + strerror(errno)); + + free(buf); + (void) close(mc_fd); + return (NULL); + } + (void) close(mc_fd); + (void) nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); + topo_mod_free(mod, buf, mcs.mcs_size); + + if (nvlist_lookup_uint8(nvl, MCINTEL_NVLIST_VERSTR, &ver) != 0) { + whinge(mod, NULL, "mc nvlist is not versioned\n"); + nvlist_free(nvl); + return (NULL); + } else if (ver != MCINTEL_NVLIST_VERS0) { + whinge(mod, NULL, "mc nvlist version mismatch\n"); + nvlist_free(nvl); + return (NULL); + } + + rc = mc_nb_create(mod, pnode, name, auth, nvl); + + nvlist_free(nvl); + return (rc); +} diff --git a/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c new file mode 100644 index 0000000000..4b4c7f984c --- /dev/null +++ b/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c @@ -0,0 +1,392 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Support function for the i86pc chip enumerator + */ + +#include <sys/types.h> +#include <stdarg.h> +#include <strings.h> +#include <sys/fm/protocol.h> + +#include "chip.h" + +/* + * Whinge a debug message via topo_mod_dprintf and increment the + * given error counter. + */ +void +whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...) +{ + va_list ap; + char buf[160]; + + if (nerr != NULL) + ++*nerr; + + va_start(ap, fmt); + (void) vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + topo_mod_dprintf(mod, "%s", buf); +} + +/* + * Given an nvpair of a limited number of data types, extract the property + * name and value and add that combination to the given node in the + * specified property group using the corresponding topo_prop_set_* function + * for the data type. Return 1 on success, otherwise 0. + */ +int +nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node) +{ + int success = 0; + int err; + char *pname = nvpair_name(nvp); + + switch (nvpair_type(nvp)) { + case DATA_TYPE_BOOLEAN_VALUE: { + boolean_t val; + + if (nvpair_value_boolean_value(nvp, &val) == 0 && + topo_prop_set_string(node, pgname, pname, + TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0) + success = 1; + break; + } + + case DATA_TYPE_UINT32: { + uint32_t val; + + if (nvpair_value_uint32(nvp, &val) == 0 && + topo_prop_set_uint32(node, pgname, pname, + TOPO_PROP_IMMUTABLE, val, &err) == 0) + success = 1; + break; + } + + case DATA_TYPE_UINT64: { + uint64_t val; + + if (nvpair_value_uint64(nvp, &val) == 0 && + topo_prop_set_uint64(node, pgname, pname, + TOPO_PROP_IMMUTABLE, val, &err) == 0) + success = 1; + break; + } + + case DATA_TYPE_UINT32_ARRAY: { + uint32_t *arrp; + uint_t nelem; + + if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 && + nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname, + TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0) + success = 1; + break; + } + + case DATA_TYPE_STRING: { + char *str; + + if (nvpair_value_string(nvp, &str) == 0 && + topo_prop_set_string(node, pgname, pname, + TOPO_PROP_IMMUTABLE, str, &err) == 0) + success = 1; + break; + } + + default: + whinge(mod, &err, "nvprop_add: Can't handle type %d for " + "'%s' in property group %s of %s node\n", + nvpair_type(nvp), pname, pgname, topo_node_name(node)); + break; + } + + return (success ? 0 : 1); +} + +/* + * Lookup string data named pname in the given kstat_t and add that + * as property named pname in the given property group pgname on the indicated + * topo node. Fill pvalp with a pointer to the string value, valid until + * kstat_close is called (or the given kstat_t is otherwise invalidated). + */ +int +add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, + const char *pgname, const char *pname, const char **pvalp) +{ + const char *pval; + kstat_named_t *k; + int err = 0; + + if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) + return (-1); + pval = k->value.str.addr.ptr; + + if (topo_prop_set_string(node, pgname, pname, + TOPO_PROP_IMMUTABLE, pval, &err) == 0) { + if (pvalp) + *pvalp = pval; + return (0); + } else { + whinge(mod, &err, "chip_strprop: failed to add '%s'\n", + pname); + return (-1); + } +} + +/* + * Lookup an int32 item named pname in the given kstat_t and add that + * as property named pname in the given property group pgname on the indicated + * topo node. Fill pvalp with the property value. + */ +int +add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, + const char *pgname, const char *pname, int32_t *pvalp) +{ + kstat_named_t *k; + int32_t pval; + int err; + + if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) + return (-1); + pval = k->value.l; + + if (topo_prop_set_int32(node, pgname, pname, + TOPO_PROP_IMMUTABLE, pval, &err) == 0) { + if (pvalp) + *pvalp = pval; + return (0); + } else { + whinge(mod, &err, "chip_longprop: failed to add '%s'\n", + pname); + return (-1); + } +} + +/* + * In a given kstat_t lookup a variable number of int32 properties named in + * const char * varargs and each each in the given property group on the + * node. Fill an array of the retrieved values. + */ +int +add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, + const char *pgname, int32_t *pvalap, ...) +{ + const char *pname; + va_list ap; + int nerr = 0; + + va_start(ap, pvalap); + while ((pname = va_arg(ap, const char *)) != NULL) { + if (add_kstat_longprop(mod, node, ksp, pgname, pname, + pvalap) != 0) + nerr++; /* have whinged elsewhere */ + + if (pvalap != NULL) + ++pvalap; + } + va_end(ap); + + return (nerr == 0 ? 0 : -1); +} + +/* + * Construct an hc scheme resource FMRI for a node named name with + * instance number inst, parented by the given parent node pnode. + */ +int +mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst, + nvlist_t *auth, nvlist_t **nvl) +{ + *nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, + inst, NULL, auth, NULL, NULL, NULL); + return (nvl != NULL ? 0 : -1); /* caller must free nvlist */ +} + +/* + * Construct a cpu scheme FMRI with the given data; the caller must free + * the allocated nvlist with nvlist_free(). + */ +nvlist_t * +cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask) +{ + int err; + nvlist_t *asru; + + if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) + return (NULL); + + err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION); + err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); + err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid); + err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask); + if (s != NULL) + err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s); + if (err != 0) { + nvlist_free(asru); + (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); + return (NULL); + } + + return (asru); +} + +/* + * Construct a mem scheme FMRI for the given unum string; the caller must + * free the allocated nvlist with nvlist_free(). + */ +nvlist_t * +mem_fmri_create(topo_mod_t *mod, const char *unum) +{ + nvlist_t *asru; + + if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) + return (NULL); + + if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 || + nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0 || + nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum) != 0) { + nvlist_free(asru); + return (NULL); + } + + return (asru); +} + +/* + * Registered method for asru computation for rank nodes. The 'node' + * argument identifies the node for which we seek an asru. The 'in' + * argument is used to select which asru we will return, as follows: + * + * - the node name must be "dimm" or "rank" + * - if 'in' is NULL then return any statically defined asru for this node + * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru + * with unum being the hc path to the dimm or rank (this method is called + * as part of dynamic asru computation for rank nodes only, but + * it is also called directly to construct a "mem" scheme asru for a dimm + * node) + * - if 'in' in addition includes an hc-specific member which specifies + * asru-physaddr or asru-offset then these are includes in the "mem" scheme + * asru as additional members physaddr and offset + */ +int +mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru) +{ + int incl_pa = 0, incl_offset = 0; + nvlist_t *hcsp, *ap; + char *unum, *scheme; + uint64_t pa, offset; + int err = 0; + + if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 || + strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) + return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); + + if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { + if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR, + &pa) == 0) + incl_pa = 1; + + if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET, + &offset) == 0) + incl_offset = 1; + } + + /* use 'fmri' to obtain resource path; could use node resource */ + if (topo_mod_nvl2str(mod, fmri, &unum) < 0) + return (-1); /* mod errno set */ + + ap = mem_fmri_create(mod, unum); + topo_mod_strfree(mod, unum); + if (ap == NULL) + return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); + + if (incl_pa) + err += nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa) != 0; + if (incl_offset) + err += nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset) != 0; + + if (err != 0) { + nvlist_free(ap); + return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); + } + + *asru = ap; + + return (0); +} + +/*ARGSUSED*/ +int +mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version, + nvlist_t *in, nvlist_t **out) +{ + nvlist_t *asru; + nvlist_t *args, *pargs; + int err; + + if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 && + strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 && + strcmp(topo_node_name(node), CS_NODE_NAME) != 0) + return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); + + if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) + return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); + + if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) { + if (err == ENOENT) { + if (topo_mod_nvdup(mod, args, &asru) < 0) + return (topo_mod_seterrno(mod, EMOD_NOMEM)); + } else { + return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); + } + } else if (mem_asru_create(mod, pargs, &asru) != 0) { + return (-1); /* mod errno already set */ + } + + if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { + nvlist_free(asru); + return (topo_mod_seterrno(mod, EMOD_NOMEM)); + } + + err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU); + err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI); + err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru); + if (err != 0) { + nvlist_free(asru); + nvlist_free(*out); + return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); + } + + nvlist_free(asru); + + return (0); +} diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com index dd200949a0..eaea3bad10 100644 --- a/usr/src/pkgdefs/SUNW0on/prototype_com +++ b/usr/src/pkgdefs/SUNW0on/prototype_com @@ -47,6 +47,8 @@ f none usr/lib/locale/C/LC_MESSAGES/AMD.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/DISK.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/FMD.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/NXGE.po 644 root bin +f none usr/lib/locale/C/LC_MESSAGES/GMCA.po 644 root bin +f none usr/lib/locale/C/LC_MESSAGES/INTEL.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/SCF.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/SUN4.po 644 root bin f none usr/lib/locale/C/LC_MESSAGES/SUN4U.po 644 root bin diff --git a/usr/src/pkgdefs/SUNWcakr.i/prototype_com b/usr/src/pkgdefs/SUNWcakr.i/prototype_com index cc550cb45d..11eae3f62d 100644 --- a/usr/src/pkgdefs/SUNWcakr.i/prototype_com +++ b/usr/src/pkgdefs/SUNWcakr.i/prototype_com @@ -63,9 +63,13 @@ d none platform/i86pc/kernel/amd64 755 root sys f none platform/i86pc/kernel/amd64/unix 755 root sys d none platform/i86pc/kernel/cpu 755 root sys d none platform/i86pc/kernel/cpu/amd64 755 root sys -f none platform/i86pc/kernel/cpu/amd64/cpu.AuthenticAMD.15 755 root sys +f none platform/i86pc/kernel/cpu/amd64/cpu_ms.AuthenticAMD 755 root sys +f none platform/i86pc/kernel/cpu/amd64/cpu_ms.AuthenticAMD.15 755 root sys +f none platform/i86pc/kernel/cpu/amd64/cpu_ms.GenuineIntel 755 root sys f none platform/i86pc/kernel/cpu/amd64/cpu.generic 755 root sys -f none platform/i86pc/kernel/cpu/cpu.AuthenticAMD.15 755 root sys +f none platform/i86pc/kernel/cpu/cpu_ms.AuthenticAMD 755 root sys +f none platform/i86pc/kernel/cpu/cpu_ms.AuthenticAMD.15 755 root sys +f none platform/i86pc/kernel/cpu/cpu_ms.GenuineIntel 755 root sys f none platform/i86pc/kernel/cpu/cpu.generic 755 root sys d none platform/i86pc/kernel/dacf 755 root sys f none platform/i86pc/kernel/dacf/consconfig_dacf 755 root sys @@ -76,6 +80,7 @@ d none platform/i86pc/kernel/drv/amd64 755 root sys f none platform/i86pc/kernel/drv/amd64/isa 755 root sys f none platform/i86pc/kernel/drv/amd64/mc-amd 755 root sys f none platform/i86pc/kernel/drv/amd64/npe 755 root sys +f none platform/i86pc/kernel/drv/amd64/intel_nb5000 755 root sys f none platform/i86pc/kernel/drv/amd64/pci 755 root sys f none platform/i86pc/kernel/drv/amd64/pit_beep 755 root sys f none platform/i86pc/kernel/drv/amd64/rootnex 755 root sys @@ -83,6 +88,8 @@ f none platform/i86pc/kernel/drv/cpudrv 755 root sys f none platform/i86pc/kernel/drv/isa 755 root sys f none platform/i86pc/kernel/drv/mc-amd 755 root sys f none platform/i86pc/kernel/drv/mc-amd.conf 644 root sys +f none platform/i86pc/kernel/drv/intel_nb5000 755 root sys +f none platform/i86pc/kernel/drv/intel_nb5000.conf 644 root sys f none platform/i86pc/kernel/drv/npe 755 root sys f none platform/i86pc/kernel/drv/pci 755 root sys f none platform/i86pc/kernel/drv/pit_beep 755 root sys diff --git a/usr/src/pkgdefs/SUNWcakrx.i/prototype_com b/usr/src/pkgdefs/SUNWcakrx.i/prototype_com index 9940fd7b73..355f8e76e1 100644 --- a/usr/src/pkgdefs/SUNWcakrx.i/prototype_com +++ b/usr/src/pkgdefs/SUNWcakrx.i/prototype_com @@ -47,10 +47,6 @@ i copyright d none platform 755 root sys d none platform/i86xpv 755 root sys d none platform/i86xpv/kernel 755 root sys -d none platform/i86xpv/kernel/cpu 755 root sys -d none platform/i86xpv/kernel/cpu/amd64 755 root sys -f none platform/i86xpv/kernel/cpu/amd64/cpu.generic 755 root sys -f none platform/i86xpv/kernel/cpu/cpu.generic 755 root sys d none platform/i86xpv/kernel/dacf 755 root sys f none platform/i86xpv/kernel/dacf/consconfig_dacf 755 root sys d none platform/i86xpv/kernel/drv 755 root sys diff --git a/usr/src/pkgdefs/SUNWfmd/prototype_i386 b/usr/src/pkgdefs/SUNWfmd/prototype_i386 index 7ba97e0552..7b69e66133 100644 --- a/usr/src/pkgdefs/SUNWfmd/prototype_i386 +++ b/usr/src/pkgdefs/SUNWfmd/prototype_i386 @@ -61,13 +61,20 @@ f none usr/lib/fm/fmd/schemes/amd64/mod.so 555 root bin f none usr/lib/fm/fmd/schemes/amd64/pkg.so 555 root bin f none usr/lib/fm/fmd/schemes/amd64/zfs.so 555 root bin f none usr/lib/fm/dict/AMD.dict 444 root bin +f none usr/lib/fm/dict/GMCA.dict 444 root bin f none usr/lib/locale/C/LC_MESSAGES/AMD.mo 444 root bin +f none usr/lib/locale/C/LC_MESSAGES/GMCA.mo 444 root bin +f none usr/lib/fm/dict/INTEL.dict 444 root bin +f none usr/lib/locale/C/LC_MESSAGES/INTEL.mo 444 root bin d none usr/platform 755 root sys d none usr/platform/i86pc 755 root sys d none usr/platform/i86pc/lib 755 root bin d none usr/platform/i86pc/lib/fm 755 root bin d none usr/platform/i86pc/lib/fm/eft 755 root bin f none usr/platform/i86pc/lib/fm/eft/amd64.eft 444 root bin +f none usr/platform/i86pc/lib/fm/eft/gcpu.eft 444 root bin +f none usr/platform/i86pc/lib/fm/eft/intel.eft 444 root bin +f none usr/platform/i86pc/lib/fm/eft/gcpu_amd.eft 444 root bin d none usr/platform/i86pc/lib/fm/topo 755 root bin d none usr/platform/i86pc/lib/fm/topo/plugins 755 root bin f none usr/platform/i86pc/lib/fm/topo/plugins/chip.so 555 root bin diff --git a/usr/src/pkgdefs/SUNWhea/prototype_i386 b/usr/src/pkgdefs/SUNWhea/prototype_i386 index 3e13405761..ec0dc2ad4b 100644 --- a/usr/src/pkgdefs/SUNWhea/prototype_i386 +++ b/usr/src/pkgdefs/SUNWhea/prototype_i386 @@ -93,6 +93,7 @@ f none usr/include/sys/mc.h 644 root bin f none usr/include/sys/mc_amd.h 644 root bin f none usr/include/sys/mca_amd.h 644 root bin f none usr/include/sys/mca_x86.h 644 root bin +f none usr/include/sys/mc_intel.h 644 root bin f none usr/include/sys/mutex_impl.h 644 root bin f none usr/include/sys/obpdefs.h 644 root bin f none usr/include/sys/pcic_reg.h 644 root bin diff --git a/usr/src/pkgdefs/SUNWmdb/prototype_i386 b/usr/src/pkgdefs/SUNWmdb/prototype_i386 index 896bbe93b6..4e0b41fe11 100644 --- a/usr/src/pkgdefs/SUNWmdb/prototype_i386 +++ b/usr/src/pkgdefs/SUNWmdb/prototype_i386 @@ -38,11 +38,13 @@ d none usr/platform/i86pc/lib 755 root bin d none usr/platform/i86pc/lib/mdb 755 root sys d none usr/platform/i86pc/lib/mdb/kvm 755 root sys d none usr/platform/i86pc/lib/mdb/kvm/amd64 755 root sys -f none usr/platform/i86pc/lib/mdb/kvm/amd64/cpu.AuthenticAMD.15.so 555 root sys +f none usr/platform/i86pc/lib/mdb/kvm/amd64/cpu.generic.so 555 root sys +f none usr/platform/i86pc/lib/mdb/kvm/amd64/cpu_ms.AuthenticAMD.15.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/amd64/pcplusmp.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/amd64/uppc.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/amd64/unix.so 555 root sys -f none usr/platform/i86pc/lib/mdb/kvm/cpu.AuthenticAMD.15.so 555 root sys +f none usr/platform/i86pc/lib/mdb/kvm/cpu.generic.so 555 root sys +f none usr/platform/i86pc/lib/mdb/kvm/cpu_ms.AuthenticAMD.15.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/pcplusmp.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/uppc.so 555 root sys f none usr/platform/i86pc/lib/mdb/kvm/unix.so 555 root sys diff --git a/usr/src/pkgdefs/SUNWmdbr/prototype_i386 b/usr/src/pkgdefs/SUNWmdbr/prototype_i386 index 775c90b985..53fae018c3 100644 --- a/usr/src/pkgdefs/SUNWmdbr/prototype_i386 +++ b/usr/src/pkgdefs/SUNWmdbr/prototype_i386 @@ -90,11 +90,13 @@ d none platform/i86pc 755 root sys d none platform/i86pc/kernel 755 root sys d none platform/i86pc/kernel/kmdb 755 root sys d none platform/i86pc/kernel/kmdb/amd64 755 root sys -f none platform/i86pc/kernel/kmdb/amd64/cpu.AuthenticAMD.15 555 root sys +f none platform/i86pc/kernel/kmdb/amd64/cpu.generic 555 root sys +f none platform/i86pc/kernel/kmdb/amd64/cpu_ms.AuthenticAMD.15 555 root sys f none platform/i86pc/kernel/kmdb/amd64/pcplusmp 555 root sys f none platform/i86pc/kernel/kmdb/amd64/uppc 555 root sys f none platform/i86pc/kernel/kmdb/amd64/unix 555 root sys -f none platform/i86pc/kernel/kmdb/cpu.AuthenticAMD.15 555 root sys +f none platform/i86pc/kernel/kmdb/cpu.generic 555 root sys +f none platform/i86pc/kernel/kmdb/cpu_ms.AuthenticAMD.15 555 root sys f none platform/i86pc/kernel/kmdb/pcplusmp 555 root sys f none platform/i86pc/kernel/kmdb/uppc 555 root sys f none platform/i86pc/kernel/kmdb/unix 555 root sys diff --git a/usr/src/pkgdefs/SUNWonmtst.i/prototype_i386 b/usr/src/pkgdefs/SUNWonmtst.i/prototype_i386 index 92fe94b555..bb7ab37885 100644 --- a/usr/src/pkgdefs/SUNWonmtst.i/prototype_i386 +++ b/usr/src/pkgdefs/SUNWonmtst.i/prototype_i386 @@ -2,9 +2,8 @@ # 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. +# Common Development and Distribution License (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. @@ -20,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" @@ -63,4 +62,7 @@ f none usr/include/sys/memtest.h 644 root sys d none usr/platform/i86pc 755 root sys d none usr/platform/i86pc/lib 755 root bin d none usr/platform/i86pc/lib/mtst 755 root bin +f none usr/platform/i86pc/lib/mtst/mtst_AuthenticAMD.so 755 root bin f none usr/platform/i86pc/lib/mtst/mtst_AuthenticAMD_15.so 755 root bin +f none usr/platform/i86pc/lib/mtst/mtst_GenuineIntel.so 755 root bin +f none usr/platform/i86pc/lib/mtst/mtst_generic.so 755 root bin diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index 3738a89d7a..9fce337324 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -5337,6 +5337,20 @@ mondo_loop() { rm -f $usr/lib/fm/prtopo # + # Remove old AMD cpu module, to be replaced by extended cpu.generic + # with AMD-specific support layered on top as a model-specific module. + # Also remove the corresponding mdb and kmdb support. Backwards BFU + # will reintroduce these files. + rm -f $root/platform/i86pc/kernel/cpu/cpu.AuthenticAMD.15 + rm -f $root/platform/i86pc/kernel/cpu/amd64/cpu.AuthenticAMD.15 + rm -f $root/usr/platform/i86pc/lib/mdb/kvm/cpu.AuthenticAMD.15.so + rm -f $root/usr/platform/i86pc/lib/mdb/kvm/amd64/cpu.AuthenticAMD.15.so + + # Remove cpu.generic from i86xpv platform + rm -f $root/platform/i86xpv/kernel/cpu/cpu.generic + rm -f $root/platform/i86xpv/kernel/cpu/amd64/cpu.generic + + # # Remove obsolete buildmnttab script. Backwards BFUs will # resurrect it by extracting it from the archives. # diff --git a/usr/src/uts/common/io/mem.c b/usr/src/uts/common/io/mem.c index 96968e9134..1be161f8f9 100644 --- a/usr/src/uts/common/io/mem.c +++ b/usr/src/uts/common/io/mem.c @@ -1134,12 +1134,12 @@ mm_get_paddr(nvlist_t *nvl, uint64_t *paddr) uint8_t version; uint64_t pa; char *scheme; + int err; #ifdef __sparc uint64_t offset; char *unum; char **serids; uint_t nserids; - int err; #endif /* Verify FMRI scheme name and version number */ @@ -1183,7 +1183,8 @@ mm_get_paddr(nvlist_t *nvl, uint64_t *paddr) } } #elif defined(__x86) - if (cmi_mc_unumtopa(NULL, nvl, &pa) == 0) + if ((err = cmi_mc_unumtopa(NULL, nvl, &pa)) != CMI_SUCCESS && + err != CMIERR_MC_PARTIALUNUMTOPA) return (EINVAL); #else #error "port me" diff --git a/usr/src/uts/common/os/cpu.c b/usr/src/uts/common/os/cpu.c index 2627de59d3..dab7f6b0af 100644 --- a/usr/src/uts/common/os/cpu.c +++ b/usr/src/uts/common/os/cpu.c @@ -2093,6 +2093,8 @@ static struct { kstat_named_t ci_model; kstat_named_t ci_step; kstat_named_t ci_clogid; + kstat_named_t ci_ncpuperchip; + kstat_named_t ci_ncoreperchip; #endif } cpu_info_template = { { "state", KSTAT_DATA_CHAR }, @@ -2116,6 +2118,8 @@ static struct { { "model", KSTAT_DATA_INT32 }, { "stepping", KSTAT_DATA_INT32 }, { "clog_id", KSTAT_DATA_INT32 }, + { "ncpu_per_chip", KSTAT_DATA_INT32 }, + { "ncore_per_chip", KSTAT_DATA_INT32 }, #endif }; @@ -2181,6 +2185,9 @@ cpu_info_kstat_update(kstat_t *ksp, int rw) 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 = cpuid_get_clogid(cp); + cpu_info_template.ci_ncpuperchip.value.l = cpuid_get_ncpu_per_chip(cp); + cpu_info_template.ci_ncoreperchip.value.l = + cpuid_get_ncore_per_chip(cp); #endif return (0); diff --git a/usr/src/uts/common/os/kcpc.c b/usr/src/uts/common/os/kcpc.c index f5cdb2ff1f..50fb62d782 100644 --- a/usr/src/uts/common/os/kcpc.c +++ b/usr/src/uts/common/os/kcpc.c @@ -432,7 +432,7 @@ kcpc_unbind(kcpc_set_t *set) * freectx() calling kcpc_free(). */ if (t == curthread && - (ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0) { + (ctx->kc_flags & KCPC_CTX_INVALID_STOPPED) == 0) { kpreempt_disable(); pcbe_ops->pcbe_allstop(); atomic_or_uint(&ctx->kc_flags, @@ -746,7 +746,7 @@ kcpc_ctx_clone(kcpc_ctx_t *ctx, kcpc_ctx_t *cctx) if (ks->ks_req[i].kr_nattrs > 0) { cks->ks_req[i].kr_attr = kmem_alloc(ks->ks_req[i].kr_nattrs * - sizeof (kcpc_attr_t), KM_SLEEP); + sizeof (kcpc_attr_t), KM_SLEEP); } for (j = 0; j < ks->ks_req[i].kr_nattrs; j++) { (void) strncpy(cks->ks_req[i].kr_attr[j].ka_name, @@ -1550,5 +1550,5 @@ kcpc_pcbe_tryload(const char *prefix, uint_t first, uint_t second, uint_t third) s[2] = third; return (modload_qualified("pcbe", - "pcbe", prefix, ".", s, 3) < 0 ? -1 : 0); + "pcbe", prefix, ".", s, 3, NULL) < 0 ? -1 : 0); } diff --git a/usr/src/uts/common/os/modctl.c b/usr/src/uts/common/os/modctl.c index 7deba7e37c..ac38b40775 100644 --- a/usr/src/uts/common/os/modctl.c +++ b/usr/src/uts/common/os/modctl.c @@ -95,7 +95,7 @@ static int moduninstall(struct modctl *); 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 *); +static struct modctl *mod_hold_installed_mod(char *, int, int, int *); static void mod_release(struct modctl *); static void mod_make_requisite(struct modctl *, struct modctl *); @@ -361,7 +361,7 @@ modctl_modload(int use_path, char *filename, int *rvp) } filenamep[MOD_MAXPATH - 1] = 0; - modp = mod_hold_installed_mod(filenamep, use_path, &retval); + modp = mod_hold_installed_mod(filenamep, use_path, 0, &retval); if (modp == NULL) goto out; @@ -2429,7 +2429,7 @@ modrload(char *subdir, char *filename, struct modctl **rmodp) fullname = filename; } - modp = mod_hold_installed_mod(fullname, 1, &retval); + modp = mod_hold_installed_mod(fullname, 1, 0, &retval); if (modp != NULL) { id = modp->mod_id; if (rmodp) { @@ -2463,16 +2463,19 @@ 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: + * Value returned in *chosen + * foo/bar.baz.1.2.3 3 + * foo/bar.baz.1.2 2 + * foo/bar.baz.1 1 + * foo/bar.baz 0 * - * 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. + * Return the module ID on success; -1 if no module was loaded. On success + * and if 'chosen' is not NULL we also return the number of suffices that + * were in the module we chose to load. */ int modload_qualified(const char *subdir, const char *p1, - const char *p2, const char *delim, uint_t suffv[], int suffc) + const char *p2, const char *delim, uint_t suffv[], int suffc, int *chosen) { char path[MOD_MAXPATH]; size_t n, resid = sizeof (path); @@ -2511,12 +2514,14 @@ modload_qualified(const char *subdir, const char *p1, for (i = suffc; i >= 0; i--) { dotv[i][0] = '\0'; - mp = mod_hold_installed_mod(path, 1, &rc); + mp = mod_hold_installed_mod(path, 1, 1, &rc); if (mp != NULL) { kmem_free(dotv, sizeof (char *) * (suffc + 1)); id = mp->mod_id; mod_release_mod(mp); + if (chosen != NULL) + *chosen = i; return (id); } } @@ -2849,7 +2854,7 @@ mod_hold_loaded_mod(struct modctl *dep, char *filename, int *status) * hold, load, and install the named module */ static struct modctl * -mod_hold_installed_mod(char *name, int usepath, int *r) +mod_hold_installed_mod(char *name, int usepath, int forcecheck, int *r) { struct modctl *modp; int retval; @@ -2858,7 +2863,7 @@ mod_hold_installed_mod(char *name, int usepath, int *r) * Verify that that module in question actually exists on disk * before allocation of module structure by mod_hold_by_name. */ - if (modrootloaded && swaploaded) { + if (modrootloaded && swaploaded || forcecheck) { if (!kobj_path_exists(name, usepath)) { *r = ENOENT; return (NULL); diff --git a/usr/src/uts/common/sys/modctl.h b/usr/src/uts/common/sys/modctl.h index 047876d2e9..3477a11cb6 100644 --- a/usr/src/uts/common/sys/modctl.h +++ b/usr/src/uts/common/sys/modctl.h @@ -539,7 +539,7 @@ extern int moddebug; extern modctl_t modules; extern int modload_qualified(const char *, - const char *, const char *, const char *, uint_t[], int); + const char *, const char *, const char *, uint_t[], int, int *); extern void mod_setup(void); extern int modload(char *, char *); diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files index 78431d1739..270dcdfd7b 100644 --- a/usr/src/uts/i86pc/Makefile.files +++ b/usr/src/uts/i86pc/Makefile.files @@ -38,6 +38,8 @@ CORE_OBJS += \ bios_call.o \ cbe.o \ cmi.o \ + cmi_hw.o \ + cms.o \ confunix.o \ cpuid.o \ cpupm.o \ diff --git a/usr/src/uts/i86pc/Makefile.i86pc.shared b/usr/src/uts/i86pc/Makefile.i86pc.shared index 4317db3f73..145001e1da 100644 --- a/usr/src/uts/i86pc/Makefile.i86pc.shared +++ b/usr/src/uts/i86pc/Makefile.i86pc.shared @@ -264,6 +264,9 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += memtest # CPU_KMODS += amd_opteron CPU_KMODS += generic_cpu +CPU_KMODS += authenticamd +CPU_KMODS += genuineintel +CPU_KMODS += intel_nb5000 # # Exec Class Modules (/kernel/exec): diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules index 28e3337262..07ee7dfe6c 100644 --- a/usr/src/uts/i86pc/Makefile.rules +++ b/usr/src/uts/i86pc/Makefile.rules @@ -49,15 +49,21 @@ $(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/authenticamd/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_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/cpu/genuineintel/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/intel_nb5000/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/%.c $(COMPILE.c) -o $@ $< @@ -225,14 +231,17 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/conf/%.c $(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/authenticamd/%.c + @($(LHEAD) $(LINT.c) $< $(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/cpu/genuineintel/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/intel_nb5000/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/i86pc/amd_opteron/Makefile b/usr/src/uts/i86pc/amd_opteron/Makefile index ab78c1d4b1..c074a57d61 100644 --- a/usr/src/uts/i86pc/amd_opteron/Makefile +++ b/usr/src/uts/i86pc/amd_opteron/Makefile @@ -17,7 +17,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -31,7 +31,7 @@ UTSBASE = ../.. # # Define the module and object file sets. # -MODULE = cpu.AuthenticAMD.15 +MODULE = cpu_ms.AuthenticAMD.15 # OBJECTS = $(CPU_AO_OBJS:%=$(OBJS_DIR)/%) LINTS = $(CPU_AO_OBJS:%.o=$(LINTS_DIR)/%.ln) @@ -69,14 +69,6 @@ ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) LDFLAGS += -dy -N misc/acpica # -# For now, disable these lint checks; maintainers should endeavor -# to investigate and remove these for maximum lint coverage. -# Please do not carry these forward to new Makefiles. -# -LINTTAGS += -erroff=E_STATIC_UNUSED -LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV - -# # Default build targets. # .KEEP_STATE: diff --git a/usr/src/uts/i86xpv/generic_cpu/Makefile b/usr/src/uts/i86pc/authenticamd/Makefile index 503b854936..cfb17dcbae 100644 --- a/usr/src/uts/i86xpv/generic_cpu/Makefile +++ b/usr/src/uts/i86pc/authenticamd/Makefile @@ -17,8 +17,6 @@ # # CDDL HEADER END # - -# # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @@ -33,22 +31,23 @@ UTSBASE = ../.. # # Define the module and object file sets. # -MODULE = cpu.generic +MODULE = cpu_ms.AuthenticAMD # -OBJECTS = $(CPU_GCPU_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(CPU_GCPU_OBJS:%.o=$(LINTS_DIR)/%.ln) +OBJECTS = $(CPU_AUTHAMD_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(CPU_AUTHAMD_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_PSM_CPU_DIR)/$(MODULE) +SRCDIR = ../cpu/authenticamd + # # Include common rules. # -include $(UTSBASE)/i86xpv/Makefile.i86xpv -include $(UTSBASE)/i86pc/cpu/Makefile.files +include ../cpu/Makefile.cpu # # Our lint library has a different name from that of the module we build. # -LINT_MODULE = generic_cpu +LINT_MODULE = authenticamd # # Define targets @@ -58,6 +57,13 @@ LINT_TARGET = $(LINT_MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # +# Overrides and additions +# +CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) +ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) +LDFLAGS += -dy -N misc/acpica + +# # Default build targets. # .KEEP_STATE: diff --git a/usr/src/uts/i86pc/cpu/Makefile.files b/usr/src/uts/i86pc/cpu/Makefile.files index 3252058682..b821ae3eaa 100644 --- a/usr/src/uts/i86pc/cpu/Makefile.files +++ b/usr/src/uts/i86pc/cpu/Makefile.files @@ -1,9 +1,8 @@ # 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. +# Common Development and Distribution License (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. @@ -18,7 +17,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -27,11 +26,17 @@ 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 + gcpu_mca.o \ + gcpu_poll.o + +CPU_AUTHAMD_OBJS = \ + authamd_main.o + +CPU_GENINTEL_OBJS = \ + gintel_main.o diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao.h b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h index e5d1b8edee..93e547d8b7 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao.h +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao.h @@ -33,7 +33,7 @@ #include <sys/mc.h> #include <sys/mca_amd.h> #include <sys/mc_amd.h> -#include <sys/cpu_module_impl.h> +#include <sys/cpu_module_ms_impl.h> #include <sys/nvpair.h> #include <sys/cyclic.h> #include <sys/errorq.h> @@ -44,17 +44,11 @@ extern "C" { #endif -#define AO_MCA_MAX_ERRORS 10 - -typedef struct ao_data ao_data_t; +#define AO_MAX_CHIPS 8 -typedef struct ao_bank_regs { - uint32_t abr_status; - uint32_t abr_addr; - uint32_t abr_misc; -} ao_bank_regs_t; +#define AO_MCA_MAX_ERRORS 10 -extern ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT]; +typedef struct ao_ms_data ao_ms_data_t; /* * Rather than using torturous conditionals, we match errors using a table of @@ -76,16 +70,27 @@ extern ao_bank_regs_t ao_bank_regs[AMD_MCA_BANK_COUNT]; #define AO_AED_PANIC_IFMCE 0x01 #define AO_AED_PANIC_ALWAYS 0x80 -#define AO_AED_F_CORRECTABLE 0x01 -#define AO_AED_F_LOFAULT_OK 0x02 -#define AO_AED_F_LINEAR 0x04 /* MCi_ADDR is a linear address */ -#define AO_AED_F_PHYSICAL 0x08 /* MCi_ADDR is a physical address */ -#define AO_AED_F_PAGEALIGNED 0x10 /* MCi_ADDR aligns to page size */ -#define AO_AED_F_L2SETWAY 0x20 /* 3:0 = way, 15/14/13/12:6 = set */ +/* + * The AO_AED_F_* flags tell us how to interpret aspects of the error + * telemetry, such as which bits of the captured address are valid for + * this error. + */ + /* MCi_ADDR ... */ +#define AO_AED_F_LINEAR 0x01 /* is a linear address */ +#define AO_AED_F_PHYSICAL 0x02 /* is a physical address */ +#define AO_AED_F_PAGEALIGNED 0x04 /* aligns to page size */ +#define AO_AED_F_L2SETWAY 0x08 /* 3:0 = way, 15/14/13/12:6 = set */ #define AO_AED_FLAGS_ADDRTYPE (AO_AED_F_LINEAR | AO_AED_F_PHYSICAL | \ AO_AED_F_PAGEALIGNED | AO_AED_F_L2SETWAY) +/* + * The AO_AED_ET_* flags group individual error dispositions into + * error types. This is used to nominate additional telemetry beyond the + * architectural bank registers to capture for this error type. + */ +#define AO_AED_ET_MEMECC 0x0001 /* Main memory ECC error */ + 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 */ @@ -99,175 +104,96 @@ typedef struct ao_error_disp { uint8_t aed_addrvalid_hi; /* most significant valid addr bit */ uint8_t aed_addrvalid_lo; /* least significant valid addr bit */ uint8_t aed_panic_when; /* extra conditions for panic */ - uint8_t aed_flags; /* AO_AED_F_* */ + uint16_t aed_flags; /* AO_AED_F_* */ + uint16_t aed_errtype; /* AO_AED_ET_* */ } 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. + * We store non-architectutal config as inherited from the BIOS to assist + * in troubleshooting. */ - -typedef struct ao_bank_logout { - uint64_t abl_status; /* Saved MCi_STATUS register */ - uint64_t abl_addr; /* Saved MCi_ADDR register */ - uint64_t abl_misc; /* Saved MCi_MISC register */ - uint8_t abl_addr_type; /* flags & AO_AED_FLAGS_ADDRTYPE */ - uint8_t abl_addr_valid_hi; /* most significant valid addr bit */ - uint8_t abl_addr_valid_lo; /* least significant valid addr bit */ -} 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. - * The NorthBridge config is stored in the chipshared structure below. - */ -typedef struct ao_bios_cfg { - uint64_t bcfg_bank_ctl[AMD_MCA_BANK_COUNT]; - uint64_t bcfg_bank_mask[AMD_MCA_BANK_COUNT]; - uint64_t bcfg_bank_misc[AMD_MCA_BANK_COUNT]; -} ao_bios_cfg_t; +struct ao_bios_cfg { + uint64_t *bcfg_bank_mask; +}; /* * 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 */ +typedef struct ao_ms_mca { + struct ao_bios_cfg ao_mca_bios_cfg; 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; +} ao_ms_mca_t; /* - * Per-chip state + * Per-chip shared state */ struct ao_chipshared { - uint32_t aos_chiprev; /* Chip revision */ + uint32_t aos_chiprev; volatile ulong_t aos_cfgonce; /* Config performed once per chip */ - kmutex_t aos_nb_poll_lock; /* Keep NB pollers from colliding */ - uint64_t aos_nb_poll_timestamp; /* Timestamp of last NB poll */ - int aos_nb_poll_owner; /* The cpuid of current NB poller */ - uint64_t aos_bcfg_nb_ctl; /* BIOS value of MC4_CTL */ - uint64_t aos_bcfg_nb_mask; /* BIOS value of MC4_MASK */ - uint64_t aos_bcfg_nb_misc; /* BIOS value of MC4_MISC */ + hrtime_t aos_nb_poll_timestamp; + cmi_hdl_t aos_nb_poll_owner; + uint64_t aos_bcfg_nb_misc; /* BIOS value of MC4_MISC MSR */ uint32_t aos_bcfg_nb_cfg; /* BIOS value of NB MCA Config */ uint32_t aos_bcfg_nb_sparectl; /* BIOS value of Online Spare Control */ uint32_t aos_bcfg_dcfg_lo; /* BIOS value of DRAM Config Low */ uint32_t aos_bcfg_dcfg_hi; /* BIOS value of DRAM Config High */ + uint32_t aos_bcfg_scrubctl; /* BIOS value of scrub control */ }; -/* Bit numbers for aos_cfgonce */ +/* Bit numbers for once-per-chip operations policed by cms_once */ enum ao_cfgonce_bitnum { AO_CFGONCE_NBMCA, + AO_CFGONCE_NBCFG, AO_CFGONCE_DRAMCFG }; /* - * Per-CPU state + * Per-CPU model-specific 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 MC ops */ - struct ao_chipshared *ao_shared; /* Shared state for the chip */ +struct ao_ms_data { + cmi_hdl_t ao_ms_hdl; + ao_ms_mca_t ao_ms_mca; + struct ao_chipshared *ao_ms_shared; + uint64_t ao_ms_hwcr_val; }; #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, int); - -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_data_t *, int); -extern void ao_mca_poll_start(void); - -extern int ao_mca_logout(ao_cpu_logout_t *, struct regs *, int *, int, - uint32_t); -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, uint8_t, uint8_t, uint32_t, - int, mc_unum_t *); -extern int ao_mc_unumtopa(ao_data_t *, mc_unum_t *, nvlist_t *, uint64_t *); +/* + * Our cms_ops operations and function prototypes for all non-NULL members. + */ +extern const cms_ops_t _cms_ops; + +extern int ao_ms_init(cmi_hdl_t, void **); +extern void ao_ms_post_startup(cmi_hdl_t); +extern void ao_ms_post_mpstartup(cmi_hdl_t); +extern uint64_t ao_ms_mcgctl_val(cmi_hdl_t, int, uint64_t); +extern boolean_t ao_ms_bankctl_skipinit(cmi_hdl_t, int); +extern uint64_t ao_ms_bankctl_val(cmi_hdl_t, int, uint64_t); +extern void ao_ms_mca_init(cmi_hdl_t, int); +extern uint64_t ao_ms_poll_ownermask(cmi_hdl_t, hrtime_t); +extern uint32_t ao_ms_error_action(cmi_hdl_t, int, int, uint64_t, + uint64_t, uint64_t, void *); +extern cms_cookie_t ao_ms_disp_match(cmi_hdl_t, int, uint64_t, uint64_t, + uint64_t, void *); +extern void ao_ms_ereport_class(cmi_hdl_t, cms_cookie_t, const char **, + const char **); +extern boolean_t ao_ms_ereport_includestack(cmi_hdl_t, cms_cookie_t); +extern void ao_ms_ereport_add_logout(cmi_hdl_t, nvlist_t *, + nv_alloc_t *, int, uint64_t, uint64_t, uint64_t, void *, void *); +extern cms_errno_t ao_ms_msrinject(cmi_hdl_t, uint_t, uint64_t); +/* + * Local functions + */ +extern void ao_chip_scrubber_enable(cmi_hdl_t, ao_ms_data_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); - -extern int ao_chip_once(ao_data_t *, enum ao_cfgonce_bitnum); +extern void ao_bankstatus_prewrite(cmi_hdl_t, ao_ms_data_t *); +extern void ao_bankstatus_postwrite(cmi_hdl_t, ao_ms_data_t *); #endif /* _KERNEL */ diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c index 9d0fa32336..178dccf52f 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_cpu.c @@ -32,19 +32,20 @@ #include <sys/sysmacros.h> #include <sys/fm/protocol.h> #include <sys/x86_archext.h> +#include <sys/pci_cfgspace.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 following two tunables are used to determine the scrubbing rates for + * the D$ and L2$. The values range from 0x00-0x16 as described in BKDG + * 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 + * this code should assign values dynamically based on cache 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 @@ -52,12 +53,6 @@ */ 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_BIOSDEFAULT, /* retain system default values */ @@ -65,19 +60,27 @@ enum { 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) +void +ao_pcicfg_write(uint_t chipid, uint_t func, uint_t reg, uint32_t val) { - nvlist_t *nvl = fm_nvlist_create(nva); + ASSERT(chipid + 24 <= 31); + ASSERT((func & 7) == func); + ASSERT((reg & 3) == 0 && reg < 256); + + cmi_pci_putl(0, chipid + 24, func, reg, 0, val); +} - fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3, - "motherboard", 0, - "chip", pg_plat_hw_instance_id(ao->ao_cpu, PGHW_CHIP), - "cpu", cpuid_get_clogid(ao->ao_cpu)); +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 (nvl); + return (cmi_pci_getl(0, chipid + 24, func, reg, 0, 0)); } + /* * 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 @@ -85,10 +88,8 @@ ao_fmri_create(ao_data_t *ao, nv_alloc_t *nva) * 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) +ao_scrubber_max(uint32_t r1, uint32_t r2) { - uint32_t r2 = (cfg & mask) >> shift; - if (r1 != 0 && r2 != 0) return (MIN(r1, r2)); @@ -96,64 +97,20 @@ ao_scrubber_max(uint32_t r1, uint32_t cfg, uint32_t mask, uint32_t shift) } /* - * 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 + * Enable the chip-specific hardware scrubbers for the D$ and L2$. 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. - * - * Where chip-select sparing is available the DRAM scrub address registers - * must not be modified while a swap is in-progress. This can't happen - * because we (the amd cpu module) take control of the online spare - * away from the BIOS when we perform NB configuration and we complete - * that operation before the memory controller driver loads. */ -int -ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen, int csdiscontig) +void +ao_chip_scrubber_enable(cmi_hdl_t hdl, ao_ms_data_t *ao) { - ao_data_t *ao = data; - chipid_t chipid = pg_plat_hw_instance_id(ao->ao_cpu, PGHW_CHIP); - uint32_t rev = cpuid_getchiprev(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, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL); - cas32(&ao_scrub_bios, 0, scrubctl); - - if (ao_scrub_policy == AO_SCRUB_BIOSDEFAULT) - 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, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, scrubctl); + chipid_t chipid = cmi_hdl_chipid(hdl); + union mcreg_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, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO); - hi = ao_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI); + ao->ao_ms_shared->aos_bcfg_scrubctl = MCREG_VAL32(&scrubctl) = + ao_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL); - 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, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO, lo); - ao_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI, hi); + if (ao_scrub_policy == AO_SCRUB_BIOSDEFAULT) + return; if (ao_scrub_rate_dcache > AMD_NB_SCRUBCTL_RATE_MAX) { cmn_err(CE_WARN, "ao_scrub_rate_dcache is too large; " @@ -167,12 +124,6 @@ ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen, int csdiscontig) 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; - } - switch (ao_scrub_policy) { case AO_SCRUB_FIXED: /* Use the system values checked above */ @@ -185,57 +136,18 @@ ao_scrubber_enable(void *data, uint64_t base, uint64_t ilen, int csdiscontig) case 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_scrubber_max(ao_scrub_rate_dcache, + MCREG_FIELD_CMN(&scrubctl, DcacheScrub)); 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); + ao_scrubber_max(ao_scrub_rate_l2cache, + MCREG_FIELD_CMN(&scrubctl, L2Scrub)); break; } -#ifdef OPTERON_ERRATUM_99 - /* - * This erratum applies on revisions D and earlier. - * - * Do not enable the dram scrubber is the chip-select ranges - * for the node are not contiguous. - */ - if (csdiscontig && !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { - cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " - "%s chip because DRAM hole is present on this node", - cpuid_getchiprevstr(ao->ao_cpu)); - ao_scrub_rate_dram = 0; - rv = 0; - } -#endif - -#ifdef OPTERON_ERRATUM_101 - /* - * This erratum applies on revisions D and earlier. - * - * 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 && ao_scrub_rate_dram != 0 && - !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { - cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " - "%s chip because DRAM memory is node-interleaved", - cpuid_getchiprevstr(ao->ao_cpu)); - 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, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, scrubctl); + MCREG_FIELD_CMN(&scrubctl, DcacheScrub) = ao_scrub_rate_dcache; + MCREG_FIELD_CMN(&scrubctl, L2Scrub) = ao_scrub_rate_l2cache; - return (rv); + ao_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, + MCREG_VAL32(&scrubctl)); } diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c index 6bef5143ee..58ebdd389a 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_main.c @@ -33,7 +33,8 @@ #include <sys/types.h> #include <sys/cmn_err.h> #include <sys/sunddi.h> -#include <sys/cpu_module_impl.h> +#include <sys/cpu_module.h> +#include <sys/cpu_module_ms_impl.h> #include <sys/cpuvar.h> #include <sys/x86_archext.h> #include <sys/kmem.h> @@ -44,7 +45,9 @@ #include "ao.h" -static struct ao_chipshared *ao_shared[NCPU]; +int ao_ms_support_disable = 0; + +static struct ao_chipshared *ao_shared[AO_MAX_CHIPS]; /* * This cpu module supports AMD family 0xf revisions B/C/D/E/F/G. If @@ -53,39 +56,38 @@ static struct ao_chipshared *ao_shared[NCPU]; */ uint_t ao_model_limit = 0x6f; -static int -ao_init(cpu_t *cp, void **datap) +int +ao_ms_init(cmi_hdl_t hdl, void **datap) { - uint_t chipid = pg_plat_hw_instance_id(CPU, PGHW_CHIP); + uint_t chipid = cmi_hdl_chipid(hdl); struct ao_chipshared *sp, *osp; - ao_data_t *ao; + ao_ms_data_t *ao; uint64_t cap; - if (cpuid_getmodel(cp) >= ao_model_limit) + if (ao_ms_support_disable || cmi_hdl_model(hdl) >= ao_model_limit) return (ENOTSUP); if (!(x86_feature & X86_MCA)) return (ENOTSUP); - cap = rdmsr(IA32_MSR_MCG_CAP); + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) + return (ENOTSUP); + if (!(cap & MCG_CAP_CTL_P)) return (ENOTSUP); - /* - * Arrange to fallback to generic.cpu if the bank count is not - * as expected. We're not silent about this - if we have X86_MCA - * and MCG_CAP_CTL_P then we appear not to be virtualized. - */ if ((cap & MCG_CAP_COUNT_MASK) != AMD_MCA_BANK_COUNT) { - cmn_err(CE_WARN, "CPU %d has %llu MCA banks; expected %u: " - "disabling AMD-specific MCA support on this CPU", - cp->cpu_id, (u_longlong_t)cap & MCG_CAP_COUNT_MASK, + cmn_err(CE_WARN, "Chip %d core %d has %llu MCA banks, " + "expected %u: disabling AMD-specific MCA support on " + "this CPU", chipid, cmi_hdl_coreid(hdl), + (u_longlong_t)cap & MCG_CAP_COUNT_MASK, AMD_MCA_BANK_COUNT); return (ENOTSUP); } - ao = *datap = kmem_zalloc(sizeof (ao_data_t), KM_SLEEP); - ao->ao_cpu = cp; + ao = *datap = kmem_zalloc(sizeof (ao_ms_data_t), KM_SLEEP); + cmi_hdl_hold(hdl); /* release in fini */ + ao->ao_ms_hdl = hdl; /* * Allocate the chipshared structure if it appears not to have been @@ -99,45 +101,50 @@ ao_init(cpu_t *cp, void **datap) if (osp != NULL) { kmem_free(sp, sizeof (struct ao_chipshared)); sp = osp; + } else { + sp->aos_chiprev = cmi_hdl_chiprev(hdl); } } - ao->ao_shared = sp; + ao->ao_ms_shared = sp; return (0); } /*ARGSUSED*/ -static void -ao_post_mpstartup(void *data) +void +ao_ms_post_mpstartup(cmi_hdl_t hdl) { (void) ddi_install_driver("mc-amd"); } -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_post_mpstartup, - 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 +cms_api_ver_t _cms_api_version = CMS_API_VERSION_0; + +const cms_ops_t _cms_ops = { + ao_ms_init, /* cms_init */ + ao_ms_post_startup, /* cms_post_startup */ + ao_ms_post_mpstartup, /* cms_post_mpstartup */ + NULL, /* cms_logout_size */ + ao_ms_mcgctl_val, /* cms_mcgctl_val */ + ao_ms_bankctl_skipinit, /* cms_bankctl_skipinit */ + ao_ms_bankctl_val, /* cms_bankctl_val */ + NULL, /* cms_bankstatus_skipinit */ + NULL, /* cms_bankstatus_val */ + ao_ms_mca_init, /* cms_mca_init */ + ao_ms_poll_ownermask, /* cms_poll_ownermask */ + NULL, /* cms_bank_logout */ + ao_ms_error_action, /* cms_error_action */ + ao_ms_disp_match, /* cms_disp_match */ + ao_ms_ereport_class, /* cms_ereport_class */ + NULL, /* cms_ereport_detector */ + ao_ms_ereport_includestack, /* cms_ereport_includestack */ + ao_ms_ereport_add_logout, /* cms_ereport_add_logout */ + ao_ms_msrinject, /* cms_msrinject */ + NULL, /* cms_fini */ }; static struct modlcpu modlcpu = { &mod_cpuops, - "AMD Athlon64/Opteron CPU Module" + "AMD Athlon64/Opteron Model-Specific Support" }; static struct modlinkage modlinkage = { @@ -149,21 +156,7 @@ static struct modlinkage modlinkage = { 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); + return (mod_install(&modlinkage)); } int @@ -175,10 +168,5 @@ _info(struct modinfo *modinfop) int _fini(void) { - int err; - - if ((err = mod_remove(&modlinkage)) == 0) - errorq_destroy(ao_mca_queue); - - return (err); + return (mod_remove(&modlinkage)); } diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c deleted file mode 100644 index b958cfc31e..0000000000 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mc.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (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, uint8_t valid_hi, uint8_t valid_lo, - 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, - valid_hi, valid_lo, 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 index 2de117b07d..09a9e919db 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca.c @@ -37,8 +37,6 @@ #include <sys/sysmacros.h> #include <sys/pghw.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> @@ -55,24 +53,15 @@ #include <sys/acpi/acpi.h> #include <sys/acpi/acpi_pci.h> #include <sys/acpica.h> +#include <sys/cpu_module.h> #include "ao.h" #include "ao_mca_disp.h" -#define AO_REVS_FG (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) +#define AO_F_REVS_FG (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) -errorq_t *ao_mca_queue; /* machine-check ereport queue */ -int ao_mca_stack_flag = 0; /* 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_DC_MISC }, - { AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR, AMD_MSR_IC_MISC }, - { AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR, AMD_MSR_BU_MISC }, - { AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR, AMD_MSR_LS_MISC }, - { AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR, AMD_MSR_NB_MISC } -}; - struct ao_ctl_init { uint32_t ctl_revmask; /* rev(s) to which this applies */ uint64_t ctl_bits; /* mca ctl reg bitmask to set */ @@ -82,40 +71,29 @@ struct ao_ctl_init { * Additional NB MCA ctl initialization for revs F and G */ static const struct ao_ctl_init ao_nb_ctl_init[] = { - { AO_REVS_FG, AMD_NB_CTL_INIT_REV_FG }, + { AO_F_REVS_FG, AMD_NB_CTL_INIT_REV_FG }, { X86_CHIPREV_UNKNOWN, 0 } }; typedef struct ao_bank_cfg { - uint_t bank_ctl; - uint_t bank_ctl_mask; uint64_t bank_ctl_init_cmn; /* Common init value */ const struct ao_ctl_init *bank_ctl_init_extra; /* Extra for each rev */ - void (*bank_misc_initfunc)(ao_data_t *, uint32_t); - uint_t bank_status; - uint_t bank_addr; + void (*bank_misc_initfunc)(cmi_hdl_t, ao_ms_data_t *, uint32_t); + uint_t bank_ctl_mask; } ao_bank_cfg_t; -static void nb_mcamisc_init(ao_data_t *, uint32_t); +static void nb_mcamisc_init(cmi_hdl_t, ao_ms_data_t *, uint32_t); static const ao_bank_cfg_t ao_bank_cfgs[] = { - { AMD_MSR_DC_CTL, AMD_MSR_DC_MASK, AMD_DC_CTL_INIT_CMN, - NULL, NULL, AMD_MSR_DC_STATUS, AMD_MSR_DC_ADDR }, - { AMD_MSR_IC_CTL, AMD_MSR_IC_MASK, AMD_IC_CTL_INIT_CMN, - NULL, NULL, AMD_MSR_IC_STATUS, AMD_MSR_IC_ADDR }, - { AMD_MSR_BU_CTL, AMD_MSR_BU_MASK, AMD_BU_CTL_INIT_CMN, - NULL, NULL, AMD_MSR_BU_STATUS, AMD_MSR_BU_ADDR }, - { AMD_MSR_LS_CTL, AMD_MSR_LS_MASK, AMD_LS_CTL_INIT_CMN, - NULL, NULL, AMD_MSR_LS_STATUS, AMD_MSR_LS_ADDR }, - { AMD_MSR_NB_CTL, AMD_MSR_NB_MASK, AMD_NB_CTL_INIT_CMN, - &ao_nb_ctl_init[0], nb_mcamisc_init, - AMD_MSR_NB_STATUS, AMD_MSR_NB_ADDR } + { AMD_DC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_DC_MASK }, + { AMD_IC_CTL_INIT_CMN, NULL, NULL, AMD_MSR_IC_MASK }, + { AMD_BU_CTL_INIT_CMN, NULL, NULL, AMD_MSR_BU_MASK }, + { AMD_LS_CTL_INIT_CMN, NULL, NULL, AMD_MSR_LS_MASK }, + { AMD_NB_CTL_INIT_CMN, &ao_nb_ctl_init[0], nb_mcamisc_init, + AMD_MSR_NB_MASK }, }; -static const ao_error_disp_t ao_disp_unknown = { - FM_EREPORT_CPU_AMD_UNKNOWN, - FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN -}; +static int ao_nbanks = sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0]); /* * This is quite awful but necessary to work around x86 system vendor's view of @@ -147,15 +125,15 @@ 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 */ + AO_MCA_R4_BIT_ERR, /* MCAX86_ERRCODE_RRRR_ERR */ + AO_MCA_R4_BIT_RD, /* MCAX86_ERRCODE_RRRR_RD */ + AO_MCA_R4_BIT_WR, /* MCAX86_ERRCODE_RRRR_WR */ + AO_MCA_R4_BIT_DRD, /* MCAX86_ERRCODE_RRRR_DRD */ + AO_MCA_R4_BIT_DWR, /* MCAX86_ERRCODE_RRRR_DWR */ + AO_MCA_R4_BIT_IRD, /* MCAX86_ERRCODE_RRRR_IRD */ + AO_MCA_R4_BIT_PREFETCH, /* MCAX86_ERRCODE_RRRR_PREFETCH */ + AO_MCA_R4_BIT_EVICT, /* MCAX86_ERRCODE_RRRR_EVICT */ + AO_MCA_R4_BIT_SNOOP /* MCAX86_ERRCODE_RRRR_SNOOP */ }; ASSERT(r4 < sizeof (ao_r4_map) / sizeof (uint16_t)); @@ -167,10 +145,10 @@ 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 */ + AO_MCA_PP_BIT_SRC, /* MCAX86_ERRCODE_PP_SRC */ + AO_MCA_PP_BIT_RES, /* MCAX86_ERRCODE_PP_RES */ + AO_MCA_PP_BIT_OBS, /* MCAX86_ERRCODE_PP_OBS */ + AO_MCA_PP_BIT_GEN /* MCAX86_ERRCODE_PP_GEN */ }; ASSERT(pp < sizeof (ao_pp_map) / sizeof (uint8_t)); @@ -182,10 +160,10 @@ 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 */ + AO_MCA_II_BIT_MEM, /* MCAX86_ERRCODE_II_MEM */ 0, - AO_MCA_II_BIT_IO, /* AMD_ERRCODE_II_IO */ - AO_MCA_II_BIT_GEN /* AMD_ERRCODE_II_GEN */ + AO_MCA_II_BIT_IO, /* MCAX86_ERRCODE_II_IO */ + AO_MCA_II_BIT_GEN /* MCAX86_ERRCODE_II_GEN */ }; ASSERT(ii < sizeof (ao_ii_map) / sizeof (uint8_t)); @@ -202,14 +180,16 @@ bit_strip(uint16_t *codep, uint16_t mask, uint16_t shift) } #define BIT_STRIP(codep, name) \ - bit_strip(codep, AMD_ERRCODE_##name##_MASK, AMD_ERRCODE_##name##_SHIFT) + bit_strip(codep, MCAX86_ERRCODE_##name##_MASK, \ + MCAX86_ERRCODE_##name##_SHIFT) +/*ARGSUSED*/ static int ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev, int bankno) { - uint16_t code = status & AMD_ERRCODE_MASK; - uint8_t extcode = (status & AMD_ERREXT_MASK) >> AMD_ERREXT_SHIFT; + uint16_t code = MCAX86_ERRCODE(status); + uint8_t extcode = AMD_EXT_ERRCODE(status); uint64_t stat_mask = aed->aed_stat_mask; uint64_t stat_mask_res = aed->aed_stat_mask_res; @@ -223,7 +203,7 @@ ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev, */ if (bankno == AMD_MCA_BANK_NB && !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && - status & AMD_BANK_STAT_OVER) { + status & MSR_MC_STATUS_OVER) { stat_mask &= ~AMD_BANK_STAT_CECC; stat_mask_res &= ~AMD_BANK_STAT_CECC; } @@ -238,13 +218,13 @@ ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev, * with the one stored in the ao_error_disp_t. */ if (AMD_ERRCODE_ISMEM(code)) { - uint8_t r4 = BIT_STRIP(&code, R4); + uint8_t r4 = BIT_STRIP(&code, RRRR); 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 r4 = BIT_STRIP(&code, RRRR); uint8_t pp = BIT_STRIP(&code, PP); uint8_t ii = BIT_STRIP(&code, II); @@ -257,95 +237,41 @@ ao_disp_match_one(const ao_error_disp_t *aed, uint64_t status, uint32_t rev, 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, uint32_t rev) +/*ARGSUSED*/ +cms_cookie_t +ao_ms_disp_match(cmi_hdl_t hdl, int banknum, uint64_t status, + uint64_t addr, uint64_t misc, void *mslogout) { + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + uint32_t rev = ao->ao_ms_shared->aos_chiprev; 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, rev, bankno)) - return (aed); + for (aed = ao_error_disp[banknum]; aed->aed_stat_mask != 0; aed++) { + if (ao_disp_match_one(aed, status, rev, banknum)) + return ((cms_cookie_t)aed); } - return (&ao_disp_unknown); + return (NULL); } +/*ARGSUSED*/ 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) +ao_ms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, + const char **cpuclsp, const char **leafclsp) { - ASSERT(chipid + 24 <= 31); - ASSERT((func & 7) == func); - ASSERT((reg & 3) == 0 && reg < 256); + const ao_error_disp_t *aed = mscookie; - return (pci_mech1_getl(0, chipid + 24, func, reg)); + if (aed != NULL) { + *cpuclsp = FM_EREPORT_CPU_AMD; + *leafclsp = aed->aed_class; + } } -/* - * ao_chip_once returns 1 if the caller should perform the operation for - * this chip, or 0 if some other core has already performed the operation. - */ - -int -ao_chip_once(ao_data_t *ao, enum ao_cfgonce_bitnum what) +static int +ao_chip_once(ao_ms_data_t *ao, enum ao_cfgonce_bitnum what) { - return (atomic_set_long_excl(&ao->ao_shared->aos_cfgonce, what) == 0 ? - 1 : 0); -} - -/* - * Setup individual bank detectors after stashing their bios settings. - * The 'donb' argument indicates whether this core should configured - * the shared NorthBridhe MSRs. - */ -static void -ao_bank_cfg(ao_data_t *ao, uint32_t rev, int donb) -{ - ao_mca_t *mca = &ao->ao_mca; - struct ao_chipshared *aos = ao->ao_shared; - ao_bios_cfg_t *bcfg = &mca->ao_mca_bios_cfg; - const ao_bank_cfg_t *bankcfg = ao_bank_cfgs; - const struct ao_ctl_init *extrap; - uint64_t mcictl; - int i; - - for (i = 0; i < AMD_MCA_BANK_COUNT; i++, bankcfg++) { - if (i == AMD_MCA_BANK_NB && donb == 0) { - bcfg->bcfg_bank_ctl[i] = 0xbaddcafe; - bcfg->bcfg_bank_mask[i] = 0xbaddcafe; - continue; - } else if (i == AMD_MCA_BANK_NB) { - aos->aos_bcfg_nb_ctl = rdmsr(bankcfg->bank_ctl); - aos->aos_bcfg_nb_mask = rdmsr(bankcfg->bank_ctl_mask); - } else { - bcfg->bcfg_bank_ctl[i] = rdmsr(bankcfg->bank_ctl); - bcfg->bcfg_bank_mask[i] = rdmsr(bankcfg->bank_ctl_mask); - } - - /* Initialize MCi_CTL register for this bank */ - mcictl = bankcfg->bank_ctl_init_cmn; - if ((extrap = bankcfg->bank_ctl_init_extra) != NULL) { - while (extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) { - if (X86_CHIPREV_MATCH(rev, extrap->ctl_revmask)) - mcictl |= extrap->ctl_bits; - extrap++; - } - } - wrmsr(bankcfg->bank_ctl, mcictl); - - /* Initialize the MCi_MISC register for this bank */ - if (bankcfg->bank_misc_initfunc != NULL) - (bankcfg->bank_misc_initfunc)(ao, rev); - } + return (atomic_set_long_excl(&ao->ao_ms_shared->aos_cfgonce, + what) == 0 ? B_TRUE : B_FALSE); } /* @@ -365,21 +291,21 @@ int ao_nb_cfg_mc4misc_noseize = 0; * to have it enabled for verification and debug work. */ static void -nb_mcamisc_init(ao_data_t *ao, uint32_t rev) +nb_mcamisc_init(cmi_hdl_t hdl, ao_ms_data_t *ao, uint32_t rev) { - uint64_t hwcr, oldhwcr; - uint64_t val; - int locked; + uint64_t val, nval; + + if (!X86_CHIPREV_MATCH(rev, AO_F_REVS_FG)) + return; - if (!X86_CHIPREV_MATCH(rev, AO_REVS_FG)) + if (cmi_hdl_rdmsr(hdl, AMD_MSR_NB_MISC, &val) != CMI_SUCCESS) return; - ao->ao_shared->aos_bcfg_nb_misc = val = rdmsr(AMD_MSR_NB_MISC); + ao->ao_ms_shared->aos_bcfg_nb_misc = val; if (ao_nb_cfg_mc4misc_noseize) return; /* stash BIOS value, but no changes */ - locked = val & AMD_NB_MISC_LOCKED; /* * The Valid bit tells us whether the CtrP bit is defined; if it @@ -389,22 +315,25 @@ nb_mcamisc_init(ao_data_t *ao, uint32_t rev) if (!(val & AMD_NB_MISC_VALID) || !(val & AMD_NB_MISC_CTRP)) return; - if (locked) { - oldhwcr = rdmsr(MSR_AMD_HWCR); - hwcr = oldhwcr | AMD_HWCR_MCI_STATUS_WREN; - wrmsr(MSR_AMD_HWCR, hwcr); - } - val |= AMD_NB_MISC_CNTEN; /* enable ECC error counting */ - val &= ~AMD_NB_MISC_ERRCOUNT_MASK; /* clear ErrCount */ - val &= ~AMD_NB_MISC_OVRFLW; /* clear Ovrflw */ - val &= ~AMD_NB_MISC_INTTYPE_MASK; /* no interrupt on overflow */ - val |= AMD_NB_MISC_LOCKED; + nval = val; + nval |= AMD_NB_MISC_CNTEN; /* enable ECC error counting */ + nval &= ~AMD_NB_MISC_ERRCOUNT_MASK; /* clear ErrCount */ + nval &= ~AMD_NB_MISC_OVRFLW; /* clear Ovrflw */ + nval &= ~AMD_NB_MISC_INTTYPE_MASK; /* no interrupt on overflow */ + nval |= AMD_NB_MISC_LOCKED; + + if (nval != val) { + uint64_t locked = val & AMD_NB_MISC_LOCKED; + + if (locked) + ao_bankstatus_prewrite(hdl, ao); - wrmsr(AMD_MSR_NB_MISC, val); + (void) cmi_hdl_wrmsr(hdl, AMD_MSR_NB_MISC, nval); - if (locked) - wrmsr(MSR_AMD_HWCR, oldhwcr); + if (locked) + ao_bankstatus_postwrite(hdl, ao); + } } /* @@ -446,7 +375,7 @@ struct ao_nb_cfg { }; static const struct ao_nb_cfg ao_cfg_extra[] = { - { AO_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG }, + { AO_F_REVS_FG, &ao_nb_cfg_add_revFG, &ao_nb_cfg_remove_revFG }, { X86_CHIPREV_UNKNOWN, NULL, NULL } }; @@ -472,7 +401,7 @@ enum { } ao_nb_watchdog_policy = AO_NB_WDOG_ENABLE_IF_DISABLED; static void -ao_nb_cfg(ao_data_t *ao, uint32_t rev) +ao_nb_cfg(ao_ms_data_t *ao, uint32_t rev) { const struct ao_nb_cfg *nbcp = &ao_cfg_extra[0]; uint_t chipid = pg_plat_hw_instance_id(CPU, PGHW_CHIP); @@ -481,8 +410,11 @@ ao_nb_cfg(ao_data_t *ao, uint32_t rev) /* * Read the NorthBridge (NB) configuration register in PCI space, * modify the settings accordingly, and store the new value back. + * Note that the stashed BIOS config value aos_bcfg_nb_cfg is used + * in ereport payload population to determine ECC syndrome type for + * memory errors. */ - ao->ao_shared->aos_bcfg_nb_cfg = val = + ao->ao_ms_shared->aos_bcfg_nb_cfg = val = ao_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG); switch (ao_nb_watchdog_policy) { @@ -532,20 +464,20 @@ ao_nb_cfg(ao_data_t *ao, uint32_t rev) } static void -ao_dram_cfg(ao_data_t *ao, uint32_t rev) +ao_dram_cfg(ao_ms_data_t *ao, uint32_t rev) { uint_t chipid = pg_plat_hw_instance_id(CPU, PGHW_CHIP); union mcreg_dramcfg_lo dcfglo; - ao->ao_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) = + ao->ao_ms_shared->aos_bcfg_dcfg_lo = MCREG_VAL32(&dcfglo) = ao_pcicfg_read(chipid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO); - ao->ao_shared->aos_bcfg_dcfg_hi = + ao->ao_ms_shared->aos_bcfg_dcfg_hi = ao_pcicfg_read(chipid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGHI); #ifdef OPTERON_ERRATUM_172 - if (X86_CHIPREV_MATCH(rev, AO_REVS_FG) && - MCREG_FIELD_revFG(&dcfglo, ParEn)) { - MCREG_FIELD_revFG(&dcfglo, ParEn) = 0; + if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG) && + MCREG_FIELD_F_revFG(&dcfglo, ParEn)) { + MCREG_FIELD_F_revFG(&dcfglo, ParEn) = 0; ao_pcicfg_write(chipid, MC_FUNC_DRAMCTL, MC_DC_REG_DRAMCFGLO, MCREG_VAL32(&dcfglo)); } @@ -566,13 +498,13 @@ int ao_nb_cfg_sparectl_noseize = 0; * any interrupt registered by the BIOS and zero all error counts. */ static void -ao_sparectl_cfg(ao_data_t *ao) +ao_sparectl_cfg(ao_ms_data_t *ao) { uint_t chipid = pg_plat_hw_instance_id(CPU, PGHW_CHIP); union mcreg_sparectl sparectl; int chan, cs; - ao->ao_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) = + ao->ao_ms_shared->aos_bcfg_nb_sparectl = MCREG_VAL32(&sparectl) = ao_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); if (ao_nb_cfg_sparectl_noseize) @@ -582,216 +514,127 @@ ao_sparectl_cfg(ao_data_t *ao) * If the BIOS has requested SMI interrupt type for ECC count * overflow for a chip-select or channel force those off. */ - MCREG_FIELD_revFG(&sparectl, EccErrInt) = 0; - MCREG_FIELD_revFG(&sparectl, SwapDoneInt) = 0; - - /* Enable writing to the EccErrCnt field */ - MCREG_FIELD_revFG(&sparectl, EccErrCntWrEn) = 1; - - /* First write, preparing for writes to EccErrCnt */ - ao_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, - MCREG_VAL32(&sparectl)); + MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; + MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; /* * Zero EccErrCnt and write this back to all chan/cs combinations. */ - MCREG_FIELD_revFG(&sparectl, EccErrCnt) = 0; + MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; + MCREG_FIELD_F_revFG(&sparectl, EccErrCnt) = 0; for (chan = 0; chan < MC_CHIP_NDRAMCHAN; chan++) { - MCREG_FIELD_revFG(&sparectl, EccErrCntDramChan) = chan; + MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = chan; for (cs = 0; cs < MC_CHIP_NCS; cs++) { - MCREG_FIELD_revFG(&sparectl, EccErrCntDramCs) = cs; + MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramCs) = cs; ao_pcicfg_write(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); } } } -/* - * 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 skipnb, - uint32_t rev) -{ - uint64_t mcg_status = rdmsr(IA32_MSR_MCG_STATUS); - int i, fatal = 0, n = 0; - - acl->acl_timestamp = gethrtime_waitfree(); - acl->acl_mcg_status = 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 status as we go. - * Also read the MCi_MISC register if MCi_STATUS.MISCV indicates that - * there is valid info there (as it will in revisions F and G for - * NorthBridge ECC errors). - */ - for (i = 0; i < AMD_MCA_BANK_COUNT; i++) { - ao_bank_logout_t *abl = &acl->acl_banks[i]; - - if (i == AMD_MCA_BANK_NB && skipnb) { - abl->abl_status = 0; - continue; - } +int ao_forgive_uc = 0; /* For test/debug only */ +int ao_forgive_pcc = 0; /* For test/debug only */ +int ao_fake_poison = 0; /* For test/debug only */ - abl->abl_addr = rdmsr(ao_bank_regs[i].abr_addr); - abl->abl_status = rdmsr(ao_bank_regs[i].abr_status); +uint32_t +ao_ms_error_action(cmi_hdl_t hdl, int ismc, int banknum, + uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) +{ + const ao_error_disp_t *aed; + uint32_t retval = 0; + uint8_t when; + int en; - if (abl->abl_status & AMD_BANK_STAT_MISCV) - abl->abl_misc = rdmsr(ao_bank_regs[i].abr_misc); - else - abl->abl_misc = 0; + if (ao_forgive_uc) + retval |= CMS_ERRSCOPE_CLEARED_UC; - if (abl->abl_status & AMD_BANK_STAT_VALID) - wrmsr(ao_bank_regs[i].abr_status, 0); - } + if (ao_forgive_pcc) + retval |= CMS_ERRSCOPE_CURCONTEXT_OK; - if (rp == NULL || !USERMODE(rp->r_cs)) - acl->acl_flags |= AO_ACL_F_PRIV; + if (ao_fake_poison && status & MSR_MC_STATUS_UC) + retval |= CMS_ERRSCOPE_POISONED; - if (ao_mca_stack_flag) - acl->acl_stackdepth = getpcstack(acl->acl_stack, FM_STK_DEPTH); - else - acl->acl_stackdepth = 0; + if (retval) + return (retval); - /* - * Clear MCG_STATUS, indicating that machine-check trap processing is - * complete. Once we do this, another machine-check trap can occur - * (if another occurs up to this point then the system will reset). - */ - if (mcg_status & MCG_STATUS_MCIP) - wrmsr(IA32_MSR_MCG_STATUS, 0); + aed = ao_ms_disp_match(hdl, banknum, status, addr, misc, mslogout); /* - * If we took a machine-check trap, then the error is fatal if the - * return instruction pointer is not valid in the global register. + * If we do not recognise the error let the cpu module apply + * the generic criteria to decide how to react. */ - if (rp != NULL && !(acl->acl_mcg_status & MCG_STATUS_RIPV)) - fatal++; + if (aed == NULL) + return (0); - /* - * 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; - uint8_t when; - - if (!(abl->abl_status & AMD_BANK_STAT_VALID)) - continue; - - aed = ao_disp_match(i, abl->abl_status, rev); - if ((when = aed->aed_panic_when) != AO_AED_PANIC_NEVER) { - if ((when & AO_AED_PANIC_ALWAYS) || - ((when & AO_AED_PANIC_IFMCE) && rp != NULL)) { - fatal++; - } - } + en = (status & MSR_MC_STATUS_EN) != 0; - /* - * If we are taking a machine-check exception and our context - * is corrupt, then we must die. - */ - if (rp != NULL && abl->abl_status & AMD_BANK_STAT_PCC) - fatal++; - - /* - * The overflow bit is set if the bank detects an error but - * the valid bit of its status register is already set - * (software has not yet read and cleared it). Enabled - * (for mc# reporting) errors overwrite disabled errors, - * uncorrectable errors overwrite correctable errors, - * uncorrectable errors are not overwritten. - * - * For the NB detector bank the overflow bit will not be - * set for repeated correctable errors on revisions D and - * earlier; it will be set on revisions E and later. - * On revision E, however, the CorrECC bit does appear - * to clear in these circumstances. Since we can enable - * machine-check exception on NB correctables we need to - * be careful here; we never enable mc# for correctable from - * other banks. - * - * Our solution will be to declare a machine-check exception - * fatal if the overflow bit is set except in the case of - * revision F on the NB detector bank for which CorrECC - * is indicated. Machine-check exception for NB correctables - * on rev E is explicitly not supported. - */ - if (rp != NULL && abl->abl_status & AMD_BANK_STAT_OVER && - !(i == AMD_MCA_BANK_NB && - X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && - abl->abl_status & AMD_BANK_STAT_CECC)) - fatal++; - - /* - * 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++; - - abl->abl_addr_type = aed->aed_flags & AO_AED_FLAGS_ADDRTYPE; - abl->abl_addr_valid_hi = aed->aed_addrvalid_hi; - abl->abl_addr_valid_lo = aed->aed_addrvalid_lo; - n++; - } + if ((when = aed->aed_panic_when) == AO_AED_PANIC_NEVER) + retval |= CMS_ERRSCOPE_IGNORE_ERR; - 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 ((when & AO_AED_PANIC_ALWAYS) || + ((when & AO_AED_PANIC_IFMCE) && (en || ismc))) + retval |= CMS_ERRSCOPE_FORCE_FATAL; - if (np != NULL) - *np = n; /* return number of errors found to caller */ + /* + * The original AMD implementation would panic on a machine check + * (not a poll) if the status overflow bit was set, with an + * exception for the case of rev F or later with an NB error + * indicating CECC. This came from the perception that the + * overflow bit was not correctly managed on rev E and earlier, for + * example that repeated correctable memeory errors did not set + * OVER but somehow clear CECC. + * + * We will leave the generic support to evaluate overflow errors + * and decide to panic on their individual merits, e.g., if PCC + * is set and so on. The AMD docs do say (as Intel does) that + * the status information is *all* from the higher-priority + * error in the case of an overflow, so it is at least as serious + * as the original and we can decide panic etc based on it. + */ - return (fatal != 0); + return (retval); } +/* + * Will need to change for family 0x10 + */ static uint_t -ao_ereport_synd(ao_data_t *ao, const ao_bank_logout_t *abl, uint_t *typep, +ao_ereport_synd(ao_ms_data_t *ao, uint64_t status, uint_t *typep, int is_nb) { if (is_nb) { - if (ao->ao_shared->aos_bcfg_nb_cfg & AMD_NB_CFG_CHIPKILLECCEN) { + if (ao->ao_ms_shared->aos_bcfg_nb_cfg & + AMD_NB_CFG_CHIPKILLECCEN) { *typep = AMD_SYNDTYPE_CHIPKILL; - return (AMD_NB_STAT_CKSYND(abl->abl_status)); + return (AMD_NB_STAT_CKSYND(status)); } else { *typep = AMD_SYNDTYPE_ECC; - return (AMD_BANK_SYND(abl->abl_status)); + return (AMD_BANK_SYND(status)); } } else { *typep = AMD_SYNDTYPE_ECC; - return (AMD_BANK_SYND(abl->abl_status)); + return (AMD_BANK_SYND(status)); } } -static void -ao_ereport_create_resource_elem(nvlist_t **nvlp, nv_alloc_t *nva, - mc_unum_t *unump, int dimmnum) +static nvlist_t * +ao_ereport_create_resource_elem(nv_alloc_t *nva, mc_unum_t *unump, int dimmnum) { - nvlist_t *snvl; - *nvlp = fm_nvlist_create(nva); /* freed by caller */ + nvlist_t *nvl, *snvl; - snvl = fm_nvlist_create(nva); + if ((nvl = fm_nvlist_create(nva)) == NULL) /* freed by caller */ + return (NULL); + + if ((snvl = fm_nvlist_create(nva)) == NULL) { + fm_nvlist_destroy(nvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE); + return (NULL); + } (void) nvlist_add_uint64(snvl, FM_FMRI_HC_SPECIFIC_OFFSET, unump->unum_offset); - fm_fmri_hc_set(*nvlp, FM_HC_SCHEME_VERSION, NULL, snvl, 5, + fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, snvl, 5, "motherboard", unump->unum_board, "chip", unump->unum_chip, "memory-controller", unump->unum_mc, @@ -799,6 +642,8 @@ ao_ereport_create_resource_elem(nvlist_t **nvlp, nv_alloc_t *nva, "rank", unump->unum_rank); fm_nvlist_destroy(snvl, nva ? FM_NVA_RETAIN : FM_NVA_FREE); + + return (nvl); } static void @@ -812,10 +657,17 @@ ao_ereport_add_resource(nvlist_t *payload, nv_alloc_t *nva, mc_unum_t *unump) for (i = 0; i < MC_UNUM_NDIMM; i++) { if (unump->unum_dimms[i] == MC_INVALNUM) break; - ao_ereport_create_resource_elem(&elems[nelems++], nva, - unump, i); + + if ((elems[nelems] = ao_ereport_create_resource_elem(nva, + unump, i)) == NULL) + break; + + nelems++; } + if (nelems == 0) + return; + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE, DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL); @@ -823,345 +675,182 @@ ao_ereport_add_resource(nvlist_t *payload, nv_alloc_t *nva, mc_unum_t *unump) fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : 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) +/*ARGSUSED*/ +void +ao_ms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, + nv_alloc_t *nva, int banknum, uint64_t status, uint64_t addr, + uint64_t misc, void *mslogout, cms_cookie_t mscookie) { - uint64_t members = aed->aed_ereport_members; - const ao_bank_logout_t *abl = &acl->acl_banks[bankno]; + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + const ao_error_disp_t *aed = mscookie; uint_t synd, syndtype; + uint64_t members; - synd = ao_ereport_synd(ao, 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 (aed == NULL) + return; - 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, NULL); - } + members = aed->aed_ereport_members; - if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_MISC) { - fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK_MISC, - DATA_TYPE_UINT64, abl->abl_misc, NULL); - } + synd = ao_ereport_synd(ao, status, &syndtype, + banknum == AMD_MCA_BANK_NB); if (members & FM_EREPORT_PAYLOAD_FLAG_SYND) { - fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SYND, + fm_payload_set(ereport, 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, + fm_payload_set(ereport, 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 = 0; - if (abl->abl_addr_type & AO_AED_F_PHYSICAL) { - 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, - abl->abl_addr_valid_hi, abl->abl_addr_valid_lo, - 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); + if (((aed->aed_flags & AO_AED_FLAGS_ADDRTYPE) == + AO_AED_F_PHYSICAL) && (status & MSR_MC_STATUS_ADDRV) && + cmi_mc_patounum(addr, aed->aed_addrvalid_hi, + aed->aed_addrvalid_lo, synd, syndtype, &unum) == + CMI_SUCCESS) + ao_ereport_add_resource(ereport, nva, &unum); } } -static void -ao_ereport_post(const ao_cpu_logout_t *acl, - int bankno, const ao_error_disp_t *aed) +/*ARGSUSED*/ +boolean_t +ao_ms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie) { - ao_data_t *ao = acl->acl_ao; - errorq_elem_t *eqep, *scr_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); - - /* - * Now try to allocate another element for scratch space and - * use that for further scratch space (eg for constructing - * nvlists to add the main ereport). If we can't reserve - * a scratch element just fallback to working within the - * element we already have, and hope for the best. All this - * is necessary because the fixed buffer nv allocator does - * not reclaim freed space and nvlist construction is - * expensive. - */ - if ((scr_eqep = errorq_reserve(ereport_errorq)) != NULL) - nva = errorq_elem_nva(ereport_errorq, scr_eqep); - else - nva = errorq_elem_nva(ereport_errorq, eqep); - } else { - ereport = fm_nvlist_create(NULL); - } + const ao_error_disp_t *aed = mscookie; - /* - * Create the "hc" scheme detector FMRI identifying this cpu - */ - 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); + if (aed == NULL) + return (0); - 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); + return ((aed->aed_ereport_members & + FM_EREPORT_PAYLOAD_FLAG_STACK) != 0); +} - /* - * We're done with 'detector' so reclaim the scratch space. - */ - if (panicstr) { - fm_nvlist_destroy(detector, FM_NVA_RETAIN); - nv_alloc_reset(nva); - } else { - fm_nvlist_destroy(detector, FM_NVA_FREE); - } +cms_errno_t +ao_ms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) +{ + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + cms_errno_t rv = CMSERR_BADMSRWRITE; - /* - * Encode the error-specific data that was saved in the logout area. - */ - ao_ereport_add_logout(ao, ereport, nva, acl, bankno, aed); + ao_bankstatus_prewrite(hdl, ao); + if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS) + rv = CMS_SUCCESS; + ao_bankstatus_postwrite(hdl, ao); - if (panicstr) { - errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); - if (scr_eqep) - errorq_cancel(ereport_errorq, scr_eqep); - } else { - (void) fm_ereport_post(ereport, EVCH_TRYHARD); - fm_nvlist_destroy(ereport, FM_NVA_FREE); - } + return (rv); } /*ARGSUSED*/ -void -ao_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe) +uint64_t +ao_ms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def) { - const ao_cpu_logout_t *acl = data; - uint32_t rev = acl->acl_ao->ao_shared->aos_chiprev; - 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, rev); - ao_ereport_post(acl, i, aed); - } - } + return ((1ULL << nbanks) - 1); } -/* - * Machine check interrupt handler - we jump here from mcetrap. - * - * A sibling core may attempt to poll the NorthBridge during the - * time we are performing the logout. So we coordinate NB access - * of all cores of the same chip via a per-chip lock. If the lock - * is held on a sibling core then we spin for it here; if the - * lock is held by the thread we have interrupted then we do - * not acquire the lock but can proceed safe in the knowledge that - * the lock owner can't actually perform any NB accesses. This - * requires that threads that take the aos_nb_poll_lock do not - * block and that they disable preemption while they hold the lock. - * It also requires that the lock be adaptive since mutex_owner does - * not work for spin locks. - */ -static int ao_mca_path1, ao_mca_path2; -int -ao_mca_trap(void *data, struct regs *rp) +boolean_t +ao_ms_bankctl_skipinit(cmi_hdl_t hdl, int banknum) { - 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]; - kmutex_t *nblock = NULL; - int tooklock = 0; - int rv; - - if (ao->ao_shared != NULL) - nblock = &ao->ao_shared->aos_nb_poll_lock; - - if (nblock && !mutex_owned(nblock)) { - /* - * The mutex is not owned by the thread we have interrupted - * (since the holder may not block or be preempted once the - * lock is acquired). We will spin for this adaptive lock. - */ - ++ao_mca_path1; - while (!mutex_tryenter(nblock)) { - while (mutex_owner(nblock) != NULL) - ; - } - tooklock = 1; - } else { - ++ao_mca_path2; - } + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); - rv = ao_mca_logout(acl, rp, NULL, 0, ao->ao_shared->aos_chiprev); + if (banknum != AMD_MCA_BANK_NB) + return (B_FALSE); - if (tooklock) - mutex_exit(&ao->ao_shared->aos_nb_poll_lock); - - return (rv); + return (ao_chip_once(ao, AO_CFGONCE_NBMCA)); } -/*ARGSUSED*/ -int -ao_mca_inject(void *data, cmi_mca_regs_t *regs, uint_t nregs) +uint64_t +ao_ms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def) { - uint64_t hwcr, oldhwcr; - int i; + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + const struct ao_ctl_init *extrap; + const ao_bank_cfg_t *bankcfg; + uint64_t mcictl; + uint32_t rev = ao->ao_ms_shared->aos_chiprev; - oldhwcr = rdmsr(MSR_AMD_HWCR); - hwcr = oldhwcr | AMD_HWCR_MCI_STATUS_WREN; - wrmsr(MSR_AMD_HWCR, hwcr); + if (banknum >= sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfgs[0])) + return (def); - for (i = 0; i < nregs; i++) - wrmsr(regs[i].cmr_msrnum, regs[i].cmr_msrval); + bankcfg = &ao_bank_cfgs[banknum]; + extrap = bankcfg->bank_ctl_init_extra; - wrmsr(MSR_AMD_HWCR, oldhwcr); - return (0); + mcictl = bankcfg->bank_ctl_init_cmn; + + while (extrap != NULL && extrap->ctl_revmask != X86_CHIPREV_UNKNOWN) { + if (X86_CHIPREV_MATCH(rev, extrap->ctl_revmask)) + mcictl |= extrap->ctl_bits; + extrap++; + } + + return (mcictl); } void -ao_mca_init(void *data) +ao_bankstatus_prewrite(cmi_hdl_t hdl, ao_ms_data_t *ao) { - ao_data_t *ao = data; - ao_mca_t *mca = &ao->ao_mca; - uint64_t cap = rdmsr(IA32_MSR_MCG_CAP); - uint32_t rev; - int donb, dodcfg; - int i; + uint64_t hwcr; - /* - * cmi_mca_init is only called during cpu startup if features include - * X86_MCA (defined as both MCA and MCE support indicated by CPUID). - * Furthermore, our ao_init function returns ENOTSUP if features - * lacked X86_MCA, IA32_MSR_MCG_CAP lacks MCG_CAP_CTL_P, or the - * cpu has an unexpected number of detector banks. - */ - ASSERT(x86_feature & X86_MCA); - ASSERT(cap & MCG_CAP_CTL_P); - ASSERT((cap & MCG_CAP_COUNT_MASK) == AMD_MCA_BANK_COUNT); + if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) + 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->ao_ms_hwcr_val = hwcr; - /* LINTED: logical expression always true */ - ASSERT(sizeof (ao_bank_cfgs) / sizeof (ao_bank_cfg_t) == - AMD_MCA_BANK_COUNT); + if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { + hwcr |= AMD_HWCR_MCI_STATUS_WREN; + (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); + } +} - rev = ao->ao_shared->aos_chiprev = cpuid_getchiprev(ao->ao_cpu); +void +ao_bankstatus_postwrite(cmi_hdl_t hdl, ao_ms_data_t *ao) +{ + uint64_t hwcr = ao->ao_ms_hwcr_val; - /* - * Must this core perform NB MCA or DRAM configuration? This must be - * done by just one core. - */ - donb = ao_chip_once(ao, AO_CFGONCE_NBMCA); - dodcfg = ao_chip_once(ao, AO_CFGONCE_DRAMCFG); + if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { + hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; + (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); + } +} - /* - * Initialize poller data, but don't start polling yet. - */ - ao_mca_poll_init(ao, donb); +void +ao_ms_mca_init(cmi_hdl_t hdl, int nbanks) +{ + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + uint32_t rev = ao->ao_ms_shared->aos_chiprev; + ao_ms_mca_t *mca = &ao->ao_ms_mca; + uint64_t *maskp; + int i; - /* - * Configure the bank MCi_CTL register to nominate which error - * types for each bank will produce a machine-check (we'll poll - * for others). Correctable error types mentioned in these MCi_CTL - * settings won't actually produce an exception unless an additional - * (and undocumented) bit is set elsewhere - the poller must still - * handle these. - */ - ao_bank_cfg(ao, rev, donb); + maskp = mca->ao_mca_bios_cfg.bcfg_bank_mask = kmem_zalloc(nbanks * + sizeof (uint64_t), KM_SLEEP); /* - * Modify the MCA NB Configuration and Dram Configuration Registers. + * Read the bank ctl mask MSRs, but only as many as we know + * certainly exist - don't calculate the register address. + * Also initialize the MCi_MISC register where required. */ - if (donb) - ao_nb_cfg(ao, rev); - if (dodcfg) - ao_dram_cfg(ao, rev); + for (i = 0; i < MIN(nbanks, ao_nbanks); i++) { + (void) cmi_hdl_rdmsr(hdl, ao_bank_cfgs[i].bank_ctl_mask, + maskp++); + if (ao_bank_cfgs[i].bank_misc_initfunc != NULL) + ao_bank_cfgs[i].bank_misc_initfunc(hdl, ao, rev); - /* - * Setup the Online Spare Control Register - */ - if (donb && X86_CHIPREV_MATCH(rev, AO_REVS_FG)) { - ao_sparectl_cfg(ao); } - /* - * Enable all error reporting banks (icache, dcache, ...). This - * enables error detection, as opposed to error reporting above. - */ - 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 < AMD_MCA_BANK_COUNT; i++) { - if (!donb) - continue; + if (ao_chip_once(ao, AO_CFGONCE_NBCFG) == B_TRUE) { + ao_nb_cfg(ao, rev); - wrmsr(ao_bank_cfgs[i].bank_status, 0ULL); + if (X86_CHIPREV_MATCH(rev, AO_F_REVS_FG)) + ao_sparectl_cfg(ao); } - wrmsr(IA32_MSR_MCG_STATUS, 0ULL); - membar_producer(); + if (ao_chip_once(ao, AO_CFGONCE_DRAMCFG) == B_TRUE) + ao_dram_cfg(ao, rev); - setcr4(getcr4() | CR4_MCE); /* enable #mc exceptions */ + ao_chip_scrubber_enable(hdl, ao); } /* @@ -1191,7 +880,7 @@ ao_acpi_find_smicmd(int *asd_port) /*ARGSUSED*/ void -ao_mca_post_init(void *data) +ao_ms_post_startup(cmi_hdl_t hdl) { const struct ao_smi_disable *asd; id_t id; @@ -1244,47 +933,4 @@ ao_mca_post_init(void *data) 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 index cec6c5c80e..f23de3565e 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.h +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.h @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -19,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,7 +38,7 @@ extern "C" { #endif #define AO_MCA_PP_BIT_SRC 0x1 -#define AO_MCA_PP_BIT_RSP 0x2 +#define AO_MCA_PP_BIT_RES 0x2 #define AO_MCA_PP_BIT_OBS 0x4 #define AO_MCA_PP_BIT_GEN 0x8 @@ -47,7 +46,7 @@ extern "C" { #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_ERR 0x001 #define AO_MCA_R4_BIT_RD 0x002 #define AO_MCA_R4_BIT_WR 0x004 #define AO_MCA_R4_BIT_DRD 0x008 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 index 2a97a900e5..0ae39b1234 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_mca_disp.in @@ -17,7 +17,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,14 +29,15 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 bus src 0 drd <39:6> mem/io lg - panic = never -flags = correctable,physical +flags = physical +errtype = # --- @@ -44,21 +45,22 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 mem - - drd <39:6> - l2 data panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -67,13 +69,14 @@ code = 0000 bus src 0 drd <39:6> mem/io lg - panic = always flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -82,6 +85,7 @@ code = 0000 mem - - drd <39:6> - l2 data panic = always flags = physical +errtype = # --- @@ -89,21 +93,22 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0000 mem - - gen <11:3> - l1 data +code = 0000 mem - - err <11:3> - l1 data panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_CECC mask off = AMD_BANK_STAT_SCRUB # ext type pp t rrrr addr ii ll tt @@ -113,13 +118,14 @@ code = 0000 mem - - ev/snp <11:6> - l1 data panic = always flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_SCRUB # ext type pp t rrrr addr ii ll tt @@ -129,28 +135,30 @@ code = 0000 mem - - ev/snp <11:6> - l1 data panic = always flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC, AMD_BANK_STAT_SCRUB mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0000 mem - - gen <11:3> - l1 data +code = 0000 mem - - err <11:3> - l1 data panic = always flags = physical +errtype = # --- desc = Main Tag Array Parity Error error = ereport.cpu.amd.dc.tag_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -159,13 +167,14 @@ code = 0000 mem - - drd/dwr <39:3> - l1 data panic = always flags = physical +errtype = # --- desc = Snoop Tag Array Parity Error error = ereport.cpu.amd.dc.stag_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -174,13 +183,14 @@ code = 0000 mem - - snp/ev <11:6> - l1 data panic = always flags = physical +errtype = # --- desc = L1 DTLB Parity Error error = ereport.cpu.amd.dc.l1tlb_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -189,13 +199,14 @@ code = 0000 tlb - - - <47:12> - l1 data panic = always flags = linear +errtype = # --- desc = L1 DTLB Parity Error (multimatch) error = ereport.cpu.amd.dc.l1tlb_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -204,13 +215,14 @@ code = 0001 tlb - - - <47:12> - l1 data panic = always flags = linear +errtype = # --- desc = L2 DTLB Parity Error error = ereport.cpu.amd.dc.l2tlb_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -219,13 +231,14 @@ code = 0000 tlb - - - <47:12> - l2 data panic = always flags = linear +errtype = # --- desc = L2 DTLB Parity Error (multimatch) error = ereport.cpu.amd.dc.l2tlb_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -234,6 +247,7 @@ code = 0001 tlb - - - <47:12> - l2 data panic = always flags = linear +errtype = # # Instruction Cache Functional Unit @@ -245,14 +259,15 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 bus src 0 ird <39:6> mem lg - panic = never -flags = correctable,physical +flags = physical +errtype = # ---- @@ -260,21 +275,22 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 mem - - ird <39:6> - l2 instr panic = never -flags = correctable,physical +flags = physical +errtype = # ---- 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 on = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -283,13 +299,14 @@ code = 0000 bus src 0 ird <39:6> mem lg - panic = always flags = physical +errtype = # --- 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 on = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -298,6 +315,7 @@ code = 0000 mem - - ird <39:6> - l2 instr panic = always flags = physical +errtype = # --- @@ -305,14 +323,15 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 mem - - ird <47:4> - l1 instr panic = never -flags = correctable,linear +flags = linear +errtype = # --- @@ -320,7 +339,7 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- @@ -328,14 +347,15 @@ code = 0000 mem - - ird <47:6> - l1 instr code = 0000 mem - - ev none - l1 instr panic = never -flags = correctable,linear +flags = linear +errtype = # --- desc = Snoop Tag Array Parity Error error = ereport.cpu.amd.ic.stag_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -345,6 +365,7 @@ code = 0000 mem - - ev none - l1 instr panic = always flags = physical +errtype = # --- @@ -352,14 +373,15 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 tlb - - - <47:12> - l1 instr panic = never -flags = correctable,linear,pagealigned +flags = linear,pagealigned +errtype = # --- @@ -367,14 +389,15 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0001 tlb - - - <47:12> - l1 instr panic = never -flags = correctable,linear,pagealigned +flags = linear,pagealigned +errtype = # --- @@ -382,14 +405,15 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 tlb - - - <47:12> - l2 instr panic = never -flags = correctable,linear +flags = linear +errtype = # --- @@ -397,21 +421,22 @@ 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 +mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0001 tlb - - - <47:12> - l2 instr panic = never -flags = correctable,linear +flags = linear +errtype = # --- desc = System Data Read Error error = ereport.cpu.amd.ic.rdde -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -420,6 +445,7 @@ code = 0000 bus src 0 ird none mem lg - panic = ifmce flags = +errtype = # # --- @@ -433,21 +459,22 @@ 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 +mask off = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0000 mem - - rd/snp/ev <39:6> - l2 gen panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -456,6 +483,7 @@ code = 0000 mem - - rd/snp/ev <39:6> - l2 gen panic = always flags = physical +errtype = # --- @@ -463,36 +491,38 @@ 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 +mask off = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0010 mem - - gen <15:0> - l2 instr +code = 0010 mem - - err <15:0> - l2 instr panic = never -flags = correctable,physical,l2setway +flags = physical,l2setway +errtype = # --- 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 on = AMD_BANK_STAT_UECC, MSR_MC_STATUS_UC, AMD_BANK_STAT_SCRUB mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0010 mem - - gen <15:0> - l2 instr +code = 0010 mem - - err <15:0> - l2 instr panic = always flags = physical,l2setway +errtype = # --- desc = L2 main tag array parity error on I$ fetch error = ereport.cpu.amd.bu.l2t_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -501,13 +531,14 @@ code = 0010 mem - - ird <15:0> - l2 instr panic = always flags = physical,l2setway +errtype = # --- desc = L2 main tag array parity error on D$ fetch error = ereport.cpu.amd.bu.l2t_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -516,13 +547,14 @@ code = 0010 mem - - drd <15:0> - l2 data panic = always flags = physical,l2setway +errtype = # --- 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 on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -531,21 +563,23 @@ code = 0010 mem - - rd/snp/ev <15:0> - l2 gen panic = always flags = physical,l2setway +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_SCRUB mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0010 mem - - gen <15:0> - l2 instr +code = 0010 mem - - err <15:0> - l2 instr panic = always flags = physical,l2setway +errtype = # --- @@ -553,7 +587,7 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- @@ -561,14 +595,15 @@ code = 0000 bus src 0 rd <39:6> mem/io lg - code = 0000 bus src 0 pf none mem/io lg - panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt @@ -578,13 +613,14 @@ code = 0000 bus src 0 pf none mem/io lg - panic = always flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -593,6 +629,7 @@ code = 0000 bus src 0 rd/pf <39:6> mem/io lg - panic = ifmce flags = physical +errtype = # # --- @@ -603,7 +640,7 @@ funcunit = ls desc = System data read error error = ereport.cpu.amd.ls.s_rde -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -612,6 +649,7 @@ code = 0000 bus src 0 rd/wr <39:6> mem/io lg - panic = ifmce flags = physical +errtype = # # --- @@ -623,29 +661,31 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0000 bus src/rsp 0 rd/wr <39:3> mem lg - +code = 0000 bus src/res 0 rd/wr <39:3> mem lg - panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0000 bus src/rsp 0 rd/wr <39:3> mem lg - +code = 0000 bus src/res 0 rd/wr <39:3> mem lg - panic = always flags = physical +errtype = # --- @@ -653,66 +693,70 @@ 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 +mask off = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 1000 bus src/rsp 0 rd/wr <39:3> mem lg - +code = 1000 bus src/res 0 rd/wr <39:3> mem lg - panic = never -flags = correctable,physical +flags = physical +errtype = # --- 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 on = MSR_MC_STATUS_UC, AMD_BANK_STAT_UECC mask off = AMD_BANK_STAT_CECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 1000 bus src/rsp 0 rd/wr <39:3> mem lg - +code = 1000 bus src/res 0 rd/wr <39:3> mem lg - panic = always flags = physical +errtype = # --- desc = Hypertransport CRC error error = ereport.cpu.amd.nb.ht_crc -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0001 bus obs 0 gen none gen lg - +code = 0001 bus obs 0 err none gen lg - panic = always flags = +errtype = # --- desc = Hypertransport Sync packet error error = ereport.cpu.amd.nb.ht_sync -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0010 bus obs 0 gen none gen lg - +code = 0010 bus obs 0 err none gen lg - panic = always flags = +errtype = # --- desc = Master Abort error = ereport.cpu.amd.nb.ma -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -721,13 +765,14 @@ code = 0011 bus src/obs 0 rd/wr <39:3> mem/io lg - panic = never flags = physical +errtype = # --- desc = Target Abort error = ereport.cpu.amd.nb.ta -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt @@ -736,63 +781,68 @@ code = 0100 bus src/obs 0 rd/wr <39:3> mem/io lg - panic = never flags = physical +errtype = # --- desc = GART Table Walk Error error = ereport.cpu.amd.nb.gart_walk -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- code = 0101 tlb - - - <39:3> - lg gen -panic = ifmce +panic = never flags = physical +errtype = # --- desc = Atomic Read/Modify/Write error error = ereport.cpu.amd.nb.rmw -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0110 bus obs 0 gen <39:3> io lg - +code = 0110 bus obs 0 err <39:3> io lg - panic = always flags = physical +errtype = # --- desc = Watchdog error (timeout) error = ereport.cpu.amd.nb.wdog -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 0111 bus gen 1 gen <39:3> gen lg - +code = 0111 bus gen 1 err <39:3> gen lg - panic = always flags = +errtype = # --- desc = DRAM Address Parity Error error = ereport.cpu.amd.nb.dramaddr_par -mask on = AMD_BANK_STAT_UC +mask on = MSR_MC_STATUS_UC mask off = AMD_BANK_STAT_CECC, AMD_BANK_STAT_UECC # ext type pp t rrrr addr ii ll tt # ------- ------- ------- ------- ------- ------- ------- ------- ----- -code = 1101 bus obs 0 gen none mem lg - +code = 1101 bus obs 0 err none mem lg - panic = always flags = +errtype = diff --git a/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c index 6fb4b8a5bd..3ea20c129b 100644 --- a/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c +++ b/usr/src/uts/i86pc/cpu/amd_opteron/ao_poll.c @@ -20,249 +20,52 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 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. + * AMD Athlon64/Opteron Model-Specific Poller Implementation */ #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 - -cyclic_id_t ao_mca_poll_cycid; -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_waitfree(); - mca->ao_mca_poll_curtrace = next; -} - /* - * Once aos_nb_poll_lock is acquired the caller must not block. The - * ao_mca_trap code also requires that once we take the aos_nb_poll_lock - * that we do not get preempted so that it can check whether the - * thread it has interrupted is the lock owner. + * Decide whether the caller should poll the NB. The decision is made + * and any poll is performed under protection of the chip-wide mutex + * enforced at the caller's level. That mutex already ensures that all + * pollers on a chip are serialized - the following is simply to + * avoid the NB poll ping-ponging between different detectors. */ -static void -ao_mca_poll_common(ao_data_t *ao, int what, int pollnb) +uint64_t +ao_ms_poll_ownermask(cmi_hdl_t hdl, hrtime_t pintvl) { - ao_mca_t *mca = &ao->ao_mca; - 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); + ao_ms_data_t *ao = cms_hdl_getcmsdata(hdl); + hrtime_t now = gethrtime_waitfree(); + hrtime_t last = ao->ao_ms_shared->aos_nb_poll_timestamp; + int dopoll = 0; + if (now - last > 2 * pintvl || last == 0) { /* - * On the first poll after re-enabling a faulty CPU we clear - * the status registers; see ao_faulted_exit() for more info. + * If no last value has been recorded assume ownership. + * Otherwise only take over if the current "owner" seems + * to be making little progress. */ - 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, !pollnb, - ao->ao_shared->aos_chiprev); - ao_mca_poll_trace(mca, what, n); - - if (fatal && cmi_panic_on_uncorrectable_error) - fm_panic("Unrecoverable Machine-Check Error (polled)"); -} - -/* - * Decide whether the caller should poll the NB. The decision is made - * and any poll is performed under protection of the chip-wide aos_nb_poll_lock, - * so that assures that no two cores poll the NB at once. To avoid the - * NB poll ping-ponging between different detectors we'll normally stick - * with the first winner. - */ -static int -ao_mca_nb_pollowner(ao_data_t *ao) -{ - uint64_t last = ao->ao_shared->aos_nb_poll_timestamp; - uint64_t now = gethrtime_waitfree(); - int rv = 0; - - ASSERT(MUTEX_HELD(&ao->ao_shared->aos_nb_poll_lock)); - - if (now - last > 2 * ao_mca_poll_interval || last == 0) { - /* Nominal owner making little progress - we'll take over */ - ao->ao_shared->aos_nb_poll_owner = CPU->cpu_id; - rv = 1; - } else if (CPU->cpu_id == ao->ao_shared->aos_nb_poll_owner) { - rv = 1; - } - - if (rv == 1) - ao->ao_shared->aos_nb_poll_timestamp = now; - - return (rv); -} - -/* - * Wrapper called from cyclic handler or from an injector poke. - * In the former case we are a CYC_LOW_LEVEL handler while in the - * latter we're in user context so in both cases we are allowed - * to block. Once we acquire the shared and adaptive aos_nb_poll_lock, however, - * we must not block or be preempted (see ao_mca_trap). - */ -static void -ao_mca_poll_wrapper(void *arg, int what) -{ - ao_data_t *ao = arg; - int pollnb; - - if (ao == NULL) - return; - - mutex_enter(&ao->ao_mca.ao_mca_poll_lock); - kpreempt_disable(); - mutex_enter(&ao->ao_shared->aos_nb_poll_lock); - - if ((pollnb = ao_mca_nb_pollowner(ao)) == 0) { - mutex_exit(&ao->ao_shared->aos_nb_poll_lock); - kpreempt_enable(); - } - - ao_mca_poll_common(ao, what, pollnb); - - if (pollnb) { - mutex_exit(&ao->ao_shared->aos_nb_poll_lock); - kpreempt_enable(); - } - mutex_exit(&ao->ao_mca.ao_mca_poll_lock); -} - -static void -ao_mca_poll_cyclic(void *arg) -{ - ao_mca_poll_wrapper(arg, AO_MPT_WHAT_CYC_ERR); -} - -void -ao_mca_poke(void *arg) -{ - ao_mca_poll_wrapper(arg, AO_MPT_WHAT_POKE_ERR); -} - -/*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) -{ - ao_data_t *ao = cpu->cpu_m.mcpu_cmidata; - - /* - * Any sibling core may begin to poll NB MCA registers - */ - if (cpu->cpu_id == ao->ao_shared->aos_nb_poll_owner) - ao->ao_shared->aos_nb_poll_timestamp = 0; -} - -void -ao_mca_poll_init(ao_data_t *ao, int donb) -{ - ao_mca_t *mca = &ao->ao_mca; - - mutex_init(&mca->ao_mca_poll_lock, NULL, MUTEX_DRIVER, NULL); - - if (donb) - mutex_init(&ao->ao_shared->aos_nb_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; + ao->ao_ms_shared->aos_nb_poll_owner = hdl; + dopoll = 1; + } else if (ao->ao_ms_shared->aos_nb_poll_owner == hdl) { + /* + * This is the current owner and it is making progress. + */ + dopoll = 1; } -} - -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; + if (dopoll) + ao->ao_ms_shared->aos_nb_poll_timestamp = now; - mutex_enter(&cpu_lock); - ao_mca_poll_cycid = cyclic_add_omni(&cyo); - mutex_exit(&cpu_lock); + return (dopoll ? -1ULL : ~(1 << AMD_MCA_BANK_NB)); } diff --git a/usr/src/uts/i86pc/cpu/authenticamd/authamd.h b/usr/src/uts/i86pc/cpu/authenticamd/authamd.h new file mode 100644 index 0000000000..15199981f7 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/authenticamd/authamd.h @@ -0,0 +1,123 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _AUTHAMD_H +#define _AUTHAMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/mca_amd.h> +#include <sys/cpu_module_ms_impl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define AUTHAMD_MAX_CHIPS 8 /* max number of chips */ +#define AUTHAMD_DRAM_NCHANNEL 2 /* dram channels per chip */ +#define AUTHAMD_DRAM_NCS 8 /* chip-selects per channel */ + +#define AUTHAMD_FAMILY_6 0x6 +#define AUTHAMD_FAMILY_F 0xf +#define AUTHAMD_FAMILY_10 0x10 + +#define AUTHAMD_SYNDTYPE_64_8 0x0 +#define AUTHAMD_SYNDTYPE_128_16 0x1 + +typedef struct authamd_data authamd_data_t; + +typedef struct authamd_error_disp { + const char *aad_subclass; + const char *aad_leafclass; + uint64_t aad_ereport_members; +} authamd_error_disp_t; + +/* + * Model-specific logout structure. + */ +#pragma pack(1) +typedef struct authamd_logout { + uint8_t aal_eccerrcnt[AUTHAMD_DRAM_NCHANNEL][AUTHAMD_DRAM_NCS]; +} authamd_logout_t; +#pragma pack() + +/* + * Per chip shared state + */ +struct authamd_chipshared { + uint_t acs_chipid; + uint_t acs_family; /* family number */ + uint32_t acs_rev; /* revision per cpuid_getchiprev */ + volatile ulong_t acs_cfgonce; /* Config performed once per chip */ +}; + +enum authamd_cfgonce_bitnum { + AUTHAMD_CFGONCE_ONLNSPRCFG, + AUTHAMD_CFGONCE_NBTHRESH +}; + +/* + * Per-CPU model-specific state + */ +struct authamd_data { + cmi_hdl_t amd_hdl; /* cpu we're associated with */ + uint64_t amd_hwcr; + struct authamd_chipshared *amd_shared; +}; + +#ifdef _KERNEL + +/* + * Our cms_ops operations and function prototypes for all non-NULL members. + */ +extern const cms_ops_t _cms_ops; + +extern int authamd_init(cmi_hdl_t, void **); +extern size_t authamd_logout_size(cmi_hdl_t); +extern uint64_t authamd_mcgctl_val(cmi_hdl_t, int, uint64_t); +extern boolean_t authamd_bankctl_skipinit(cmi_hdl_t, int); +extern uint64_t authamd_bankctl_val(cmi_hdl_t, int, uint64_t); +extern void authamd_mca_init(cmi_hdl_t, int); +extern void authamd_bank_logout(cmi_hdl_t, int, uint64_t, uint64_t, + uint64_t, void *); +extern uint32_t authamd_error_action(cmi_hdl_t, int, int, uint64_t, + uint64_t, uint64_t, void *); +extern cms_cookie_t authamd_disp_match(cmi_hdl_t, int, uint64_t, uint64_t, + uint64_t, void *); +extern void authamd_ereport_class(cmi_hdl_t, cms_cookie_t, const char **, + const char **); +extern void authamd_ereport_add_logout(cmi_hdl_t, nvlist_t *, + nv_alloc_t *, int, uint64_t, uint64_t, uint64_t, void *, cms_cookie_t); +extern cms_errno_t authamd_msrinject(cmi_hdl_t, uint_t, uint64_t); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _AUTHAMD_H */ diff --git a/usr/src/uts/i86pc/cpu/authenticamd/authamd_main.c b/usr/src/uts/i86pc/cpu/authenticamd/authamd_main.c new file mode 100644 index 0000000000..ef158a3c86 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/authenticamd/authamd_main.c @@ -0,0 +1,895 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * "Generic AMD" model-specific support. If no more-specific support can + * be found, or such modules declines to initialize, then for AuthenticAMD + * cpus this module can have a crack at providing some AMD model-specific + * support that at least goes beyond common MCA architectural features + * if not down to the nitty-gritty level for a particular model. We + * are layered on top of a cpu module, likely cpu.generic, so there is no + * need for us to perform common architecturally-accessible functions. + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/modctl.h> +#include <sys/cpu_module.h> +#include <sys/mca_x86.h> +#include <sys/pci_cfgspace.h> +#include <sys/x86_archext.h> +#include <sys/mc_amd.h> +#include <sys/fm/protocol.h> +#include <sys/fm/cpu/GENAMD.h> +#include <sys/nvpair.h> +#include <sys/controlregs.h> +#include <sys/pghw.h> +#include <sys/sunddi.h> +#include <sys/cpu_module_ms_impl.h> + +#include "authamd.h" + +int authamd_ms_support_disable = 0; + +#define AUTHAMD_F_REVS_BCDE \ + (X86_CHIPREV_AMD_F_REV_B | X86_CHIPREV_AMD_F_REV_C0 | \ + X86_CHIPREV_AMD_F_REV_CG | X86_CHIPREV_AMD_F_REV_D | \ + X86_CHIPREV_AMD_F_REV_E) + +#define AUTHAMD_F_REVS_FG \ + (X86_CHIPREV_AMD_F_REV_F | X86_CHIPREV_AMD_F_REV_G) + +#define AUTHAMD_10_REVS_AB \ + (X86_CHIPREV_AMD_10_REV_A | X86_CHIPREV_AMD_10_REV_B) + +/* + * Bitmasks of support for various features. Try to enable features + * via inclusion in one of these bitmasks and check that at the + * feature imlementation - that way new family support may often simply + * simply need to update these bitmasks. + */ + +/* + * Families that this module will provide some model-specific + * support for (if no more-specific module claims it first). + * We try to support whole families rather than differentiate down + * to revision. + */ +#define AUTHAMD_SUPPORTED(fam) \ + ((fam) == AUTHAMD_FAMILY_6 || (fam) == AUTHAMD_FAMILY_F || \ + (fam) == AUTHAMD_FAMILY_10) + +/* + * Families/revisions for which we can recognise main memory ECC errors. + */ +#define AUTHAMD_MEMECC_RECOGNISED(rev) \ + (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ + X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) + +/* + * Families/revisions that have an Online Spare Control Register + */ +#define AUTHAMD_HAS_ONLINESPARECTL(rev) \ + (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) || \ + X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) + +/* + * Families/revisions that have a NB misc register or registers - + * evaluates to 0 if no support, otherwise the number of MC4_MISCj. + */ +#define AUTHAMD_NBMISC_NUM(rev) \ + (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F)? 1 : \ + (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A) ? 3 : 0)) + +/* + * Families/revision for which we wish not to machine check for GART + * table walk errors - bit 10 of NB CTL. + */ +#define AUTHAMD_NOGARTTBLWLK_MC(rev) \ + (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_B) || \ + X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_10_REV_A)) + +/* + * We recognise main memory ECC errors for AUTHAMD_MEMECC_RECOGNISED + * revisions as: + * + * - being reported by the NB + * - being a compound bus/interconnect error (external to chip) + * - having LL of LG + * - having II of MEM (but could still be a master/target abort) + * - having CECC or UECC set + * + * We do not check the extended error code (first nibble of the + * model-specific error code on AMD) since this has changed from + * family 0xf to family 0x10 (ext code 0 now reserved on family 0x10). + * Instead we use CECC/UECC to separate off the master/target + * abort cases. + * + * We insist that the detector be the NorthBridge bank; although + * IC/DC can report some main memory errors, they do not capture + * an address at sufficient resolution to be useful and the NB will + * report most errors. + */ +#define AUTHAMD_IS_MEMECCERR(bank, status) \ + ((bank) == AMD_MCA_BANK_NB && \ + MCAX86_ERRCODE_ISBUS_INTERCONNECT(MCAX86_ERRCODE(status)) && \ + MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ + MCAX86_ERRCODE_II(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_II_MEM && \ + ((status) & (AMD_BANK_STAT_CECC | AMD_BANK_STAT_UECC))) + +static authamd_error_disp_t authamd_memce_disp = { + FM_EREPORT_CPU_GENAMD, + FM_EREPORT_CPU_GENAMD_MEM_CE, + FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE +}; + +static authamd_error_disp_t authamd_memue_disp = { + FM_EREPORT_CPU_GENAMD, + FM_EREPORT_CPU_GENAMD_MEM_UE, + FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE +}; + +static authamd_error_disp_t authamd_ckmemce_disp = { + FM_EREPORT_CPU_GENAMD, + FM_EREPORT_CPU_GENAMD_CKMEM_CE, + FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE +}; + +static authamd_error_disp_t authamd_ckmemue_disp = { + FM_EREPORT_CPU_GENAMD, + FM_EREPORT_CPU_GENAMD_CKMEM_UE, + FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE +}; + +/* + * We recognise GART walk errors as: + * + * - being reported by the NB + * - being a compound TLB error + * - having LL of LG and TT of GEN + * - having UC set + * - possibly having PCC set (if source CPU) + */ +#define AUTHAMD_IS_GARTERR(bank, status) \ + ((bank) == AMD_MCA_BANK_NB && \ + MCAX86_ERRCODE_ISTLB(MCAX86_ERRCODE(status)) && \ + MCAX86_ERRCODE_LL(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_LL_LG && \ + MCAX86_ERRCODE_TT(MCAX86_ERRCODE(status)) == MCAX86_ERRCODE_TT_GEN && \ + (status) & MSR_MC_STATUS_UC) + +static authamd_error_disp_t authamd_gart_disp = { + FM_EREPORT_CPU_GENAMD, /* use generic subclass */ + FM_EREPORT_CPU_GENADM_GARTTBLWLK, /* use generic leafclass */ + 0 /* no additional payload */ +}; + + +static struct authamd_chipshared *authamd_shared[AUTHAMD_MAX_CHIPS]; + +static int +authamd_chip_once(authamd_data_t *authamd, enum authamd_cfgonce_bitnum what) +{ + return (atomic_set_long_excl(&authamd->amd_shared->acs_cfgonce, + what) == 0 ? B_TRUE : B_FALSE); +} + +static void +authamd_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); + + cmi_pci_putl(0, chipid + 24, func, reg, 0, val); +} + +static uint32_t +authamd_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 (cmi_pci_getl(0, chipid + 24, func, reg, 0, 0)); +} + +void +authamd_bankstatus_prewrite(cmi_hdl_t hdl, authamd_data_t *authamd) +{ + uint64_t hwcr; + + if (cmi_hdl_rdmsr(hdl, MSR_AMD_HWCR, &hwcr) != CMI_SUCCESS) + return; + + authamd->amd_hwcr = hwcr; + + if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { + hwcr |= AMD_HWCR_MCI_STATUS_WREN; + (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); + } +} + +void +authamd_bankstatus_postwrite(cmi_hdl_t hdl, authamd_data_t *authamd) +{ + uint64_t hwcr = authamd->amd_hwcr; + + if (!(hwcr & AMD_HWCR_MCI_STATUS_WREN)) { + hwcr &= ~AMD_HWCR_MCI_STATUS_WREN; + (void) cmi_hdl_wrmsr(hdl, MSR_AMD_HWCR, hwcr); + } +} + +/* + * Read EccCnt repeatedly for all possible channel/chip-select combos: + * + * - read sparectl register + * - if EccErrCntWrEn is set, clear that bit in the just-read value + * and write it back to sparectl; this *may* clobber the EccCnt + * for the channel/chip-select combination currently selected, so + * we leave this bit clear if we had to clear it + * - cycle through all channel/chip-select combinations writing each + * combination to sparectl before reading the register back for + * EccCnt for that combination; since EccErrCntWrEn is clear + * the writes to select what count to read will not themselves + * zero any counts + */ +static int +authamd_read_ecccnt(authamd_data_t *authamd, struct authamd_logout *msl) +{ + union mcreg_sparectl sparectl; + uint_t chipid = authamd->amd_shared->acs_chipid; + uint_t family = authamd->amd_shared->acs_family; + uint32_t rev = authamd->amd_shared->acs_rev; + int chan, cs; + + /* + * Check for feature support; this macro will test down to the + * family revision number, whereafter we'll switch on family + * assuming that future revisions will use the same register + * format. + */ + if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) { + bzero(&msl->aal_eccerrcnt, sizeof (msl->aal_eccerrcnt)); + return (0); + } + + MCREG_VAL32(&sparectl) = + authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); + + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 0; + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 0; + break; + } + + for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = + chan; + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = + chan; + break; + } + + for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, + EccErrCntDramCs) = cs; + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, + EccErrCntDramCs) = cs; + break; + } + + authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, + MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); + + MCREG_VAL32(&sparectl) = authamd_pcicfg_read(chipid, + MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); + + switch (family) { + case AUTHAMD_FAMILY_F: + msl->aal_eccerrcnt[chan][cs] = + MCREG_FIELD_F_revFG(&sparectl, EccErrCnt); + break; + case AUTHAMD_FAMILY_10: + msl->aal_eccerrcnt[chan][cs] = + MCREG_FIELD_10_revAB(&sparectl, EccErrCnt); + break; + } + } + } + + return (1); +} + +/* + * Clear EccCnt for all possible channel/chip-select combos: + * + * - set EccErrCntWrEn in sparectl, if necessary + * - write 0 to EccCnt for all channel/chip-select combinations + * - clear EccErrCntWrEn + * + * If requested also disable the interrupts taken on counter overflow + * and on swap done. + */ +static void +authamd_clear_ecccnt(authamd_data_t *authamd, boolean_t clrint) +{ + union mcreg_sparectl sparectl; + uint_t chipid = authamd->amd_shared->acs_chipid; + uint_t family = authamd->amd_shared->acs_family; + uint32_t rev = authamd->amd_shared->acs_rev; + int chan, cs; + + if (!AUTHAMD_HAS_ONLINESPARECTL(rev)) + return; + + MCREG_VAL32(&sparectl) = + authamd_pcicfg_read(chipid, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); + + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, EccErrCntWrEn) = 1; + if (clrint) { + MCREG_FIELD_F_revFG(&sparectl, EccErrInt) = 0; + MCREG_FIELD_F_revFG(&sparectl, SwapDoneInt) = 0; + } + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, EccErrCntWrEn) = 1; + if (clrint) { + MCREG_FIELD_10_revAB(&sparectl, EccErrInt) = 0; + MCREG_FIELD_10_revAB(&sparectl, SwapDoneInt) = 0; + } + break; + } + + authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, + MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); + + for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, EccErrCntDramChan) = + chan; + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, EccErrCntDramChan) = + chan; + break; + } + + for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { + switch (family) { + case AUTHAMD_FAMILY_F: + MCREG_FIELD_F_revFG(&sparectl, + EccErrCntDramCs) = cs; + MCREG_FIELD_F_revFG(&sparectl, + EccErrCnt) = 0; + break; + + case AUTHAMD_FAMILY_10: + MCREG_FIELD_10_revAB(&sparectl, + EccErrCntDramCs) = cs; + MCREG_FIELD_10_revAB(&sparectl, + EccErrCnt) = 0; + break; + } + + authamd_pcicfg_write(chipid, MC_FUNC_MISCCTL, + MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); + } + } +} + +/* + * cms_init entry point. + * + * This module provides broad model-specific support for AMD families + * 0x6, 0xf and 0x10. Future families will have to be evaluated once their + * documentation is available. + */ +int +authamd_init(cmi_hdl_t hdl, void **datap) +{ + uint_t chipid = cmi_hdl_chipid(hdl); + struct authamd_chipshared *sp, *osp; + uint_t family = cmi_hdl_family(hdl); + authamd_data_t *authamd; + uint64_t cap; + + if (authamd_ms_support_disable || !AUTHAMD_SUPPORTED(family)) + return (ENOTSUP); + + if (!(x86_feature & X86_MCA)) + return (ENOTSUP); + + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) + return (ENOTSUP); + + if (!(cap & MCG_CAP_CTL_P)) + return (ENOTSUP); + + authamd = *datap = kmem_zalloc(sizeof (authamd_data_t), KM_SLEEP); + cmi_hdl_hold(hdl); /* release in fini */ + authamd->amd_hdl = hdl; + + if ((sp = authamd_shared[chipid]) == NULL) { + sp = kmem_zalloc(sizeof (struct authamd_chipshared), KM_SLEEP); + osp = atomic_cas_ptr(&authamd_shared[chipid], NULL, sp); + if (osp != NULL) { + kmem_free(sp, sizeof (struct authamd_chipshared)); + sp = osp; + } else { + sp->acs_chipid = chipid; + sp->acs_family = family; + sp->acs_rev = cmi_hdl_chiprev(hdl); + } + } + authamd->amd_shared = sp; + + return (0); +} + +/* + * cms_logout_size entry point. + */ +/*ARGSUSED*/ +size_t +authamd_logout_size(cmi_hdl_t hdl) +{ + return (sizeof (struct authamd_logout)); +} + +/* + * cms_mcgctl_val entry point + * + * Instead of setting all bits to 1 we can set just those for the + * error detector banks known to exist. + */ +/*ARGSUSED*/ +uint64_t +authamd_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t proposed) +{ + return (nbanks < 64 ? (1ULL << nbanks) - 1 : proposed); +} + +/* + * cms_bankctl_skipinit entry point + * + * On K6 we do not initialize MC0_CTL since, reportedly, this bank (for DC) + * may produce spurious machine checks. + */ +/*ARGSUSED*/ +boolean_t +authamd_bankctl_skipinit(cmi_hdl_t hdl, int bank) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + + return (authamd->amd_shared->acs_family == AUTHAMD_FAMILY_6 && + bank == 0 ? B_TRUE : B_FALSE); +} + +/* + * cms_bankctl_val entry point + */ +uint64_t +authamd_bankctl_val(cmi_hdl_t hdl, int bank, uint64_t proposed) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + uint32_t rev = authamd->amd_shared->acs_rev; + uint64_t val = proposed; + + /* + * The Intel MCA says we can write all 1's to enable #MC for + * all errors, and AMD docs say much the same. But, depending + * perhaps on other config registers, taking machine checks + * for some errors such as GART TLB errors and master/target + * aborts may be bad - they set UC and sometime also PCC, but + * we should not always panic for these error types. + * + * Our cms_error_action entry point can suppress such panics, + * however we can also use the cms_bankctl_val entry point to + * veto enabling of some of the known villains in the first place. + */ + if (bank == AMD_MCA_BANK_NB && AUTHAMD_NOGARTTBLWLK_MC(rev)) + val &= ~AMD_NB_EN_GARTTBLWK; + + return (val); +} + +/* + * cms_mca_init entry point. + */ +/*ARGSUSED*/ +void +authamd_mca_init(cmi_hdl_t hdl, int nbanks) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + uint32_t rev = authamd->amd_shared->acs_rev; + + /* + * On chips with a NB online spare control register take control + * and clear ECC counts. + */ + if (AUTHAMD_HAS_ONLINESPARECTL(rev) && + authamd_chip_once(authamd, AUTHAMD_CFGONCE_ONLNSPRCFG)) { + authamd_clear_ecccnt(authamd, B_TRUE); + } + + /* + * And since we are claiming the telemetry stop the BIOS receiving + * an SMI on NB threshold overflow. + */ + if (AUTHAMD_NBMISC_NUM(rev) && + authamd_chip_once(authamd, AUTHAMD_CFGONCE_NBTHRESH)) { + union mcmsr_nbmisc nbm; + int i; + + authamd_bankstatus_prewrite(hdl, authamd); + + for (i = 0; i < AUTHAMD_NBMISC_NUM(rev); i++) { + if (cmi_hdl_rdmsr(hdl, MC_MSR_NB_MISC(i), + (uint64_t *)&nbm) != CMI_SUCCESS) + continue; + + if (X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_F) && + MCMSR_FIELD_F_revFG(&nbm, mcmisc_Valid) && + MCMSR_FIELD_F_revFG(&nbm, mcmisc_CntP)) { + MCMSR_FIELD_F_revFG(&nbm, mcmisc_IntType) = 0; + } else if (X86_CHIPREV_ATLEAST(rev, + X86_CHIPREV_AMD_10_REV_A) && + MCMSR_FIELD_10_revAB(&nbm, mcmisc_Valid) && + MCMSR_FIELD_10_revAB(&nbm, mcmisc_CntP)) { + MCMSR_FIELD_10_revAB(&nbm, mcmisc_IntType) = 0; + } + + (void) cmi_hdl_wrmsr(hdl, MC_MSR_NB_MISC(i), + MCMSR_VAL(&nbm)); + } + + authamd_bankstatus_postwrite(hdl, authamd); + } +} + +/* + * cms_bank_logout entry point. + */ +/*ARGSUSED*/ +void +authamd_bank_logout(cmi_hdl_t hdl, int bank, uint64_t status, + uint64_t addr, uint64_t misc, void *mslogout) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + struct authamd_logout *msl = mslogout; + uint32_t rev = authamd->amd_shared->acs_rev; + + if (msl == NULL) + return; + + /* + * For main memory ECC errors on revisions with an Online Spare + * Control Register grab the ECC counts by channel and chip-select + * and reset them to 0. + */ + if (AUTHAMD_MEMECC_RECOGNISED(rev) && + AUTHAMD_IS_MEMECCERR(bank, status) && + AUTHAMD_HAS_ONLINESPARECTL(rev)) { + if (authamd_read_ecccnt(authamd, msl)) + authamd_clear_ecccnt(authamd, B_FALSE); + } +} + +/* + * cms_error_action entry point + */ + +int authamd_forgive_uc = 0; /* For test/debug only */ +int authamd_forgive_pcc = 0; /* For test/debug only */ +int authamd_fake_poison = 0; /* For test/debug only */ + +/*ARGSUSED*/ +uint32_t +authamd_error_action(cmi_hdl_t hdl, int ismc, int bank, + uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) +{ + authamd_error_disp_t *disp; + uint32_t rv = 0; + + if (authamd_forgive_uc) + rv |= CMS_ERRSCOPE_CLEARED_UC; + + if (authamd_forgive_pcc) + rv |= CMS_ERRSCOPE_CURCONTEXT_OK; + + if (authamd_fake_poison && status & MSR_MC_STATUS_UC) + rv |= CMS_ERRSCOPE_POISONED; + + if (rv) + return (rv); + + disp = authamd_disp_match(hdl, bank, status, addr, misc, mslogout); + + if (disp == &authamd_gart_disp) { + /* + * GART walk errors set UC and possibly PCC (if source CPU) + * but should not be regarded as terminal. + */ + return (CMS_ERRSCOPE_IGNORE_ERR); + } + + /* + * May also want to consider master abort and target abort. These + * also set UC and PCC (if src CPU) but the requester gets -1 + * and I believe the IO stuff in Solaris will handle that. + */ + + return (rv); +} + +/* + * cms_disp_match entry point + */ +/*ARGSUSED*/ +cms_cookie_t +authamd_disp_match(cmi_hdl_t hdl, int bank, uint64_t status, + uint64_t addr, uint64_t misc, void *mslogout) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + /* uint16_t errcode = MCAX86_ERRCODE(status); */ + uint16_t exterrcode = AMD_EXT_ERRCODE(status); + uint32_t rev = authamd->amd_shared->acs_rev; + + /* + * Recognise main memory ECC errors + */ + if (AUTHAMD_MEMECC_RECOGNISED(rev) && + AUTHAMD_IS_MEMECCERR(bank, status)) { + if (status & AMD_BANK_STAT_CECC) { + return (exterrcode == 0 ? &authamd_memce_disp : + &authamd_ckmemce_disp); + } else if (status & AMD_BANK_STAT_UECC) { + return (exterrcode == 0 ? &authamd_memue_disp : + &authamd_ckmemue_disp); + } + } + + /* + * Recognise GART walk errors + */ + if (AUTHAMD_NOGARTTBLWLK_MC(rev) && AUTHAMD_IS_GARTERR(bank, status)) + return (&authamd_gart_disp); + + return (NULL); +} + +/* + * cms_ereport_class entry point + */ +/*ARGSUSED*/ +void +authamd_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, + const char **cpuclsp, const char **leafclsp) +{ + const authamd_error_disp_t *aed = mscookie; + + if (aed == NULL) + return; + + if (aed->aad_subclass != NULL) + *cpuclsp = aed->aad_subclass; + if (aed->aad_leafclass != NULL) + *leafclsp = aed->aad_leafclass; +} + +/*ARGSUSED*/ +static void +authamd_ereport_add_resource(cmi_hdl_t hdl, authamd_data_t *authamd, + nvlist_t *ereport, nv_alloc_t *nva, void *mslogout) +{ + nvlist_t *elems[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; + uint8_t counts[AUTHAMD_DRAM_NCHANNEL * AUTHAMD_DRAM_NCS]; + authamd_logout_t *msl; + nvlist_t *nvl; + int nelems = 0; + int i, chan, cs; + + if ((msl = mslogout) == NULL) + return; + + for (chan = 0; chan < AUTHAMD_DRAM_NCHANNEL; chan++) { + for (cs = 0; cs < AUTHAMD_DRAM_NCS; cs++) { + if (msl->aal_eccerrcnt[chan][cs] == 0) + continue; + + if ((nvl = fm_nvlist_create(nva)) == NULL) + continue; + + elems[nelems] = nvl; + counts[nelems++] = msl->aal_eccerrcnt[chan][cs]; + + fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 5, + "motherboard", 0, + "chip", authamd->amd_shared->acs_chipid, + "memory-controller", 0, + "dram-channel", chan, + "chip-select", cs); + } + } + + if (nelems == 0) + return; + + fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE, + DATA_TYPE_NVLIST_ARRAY, nelems, elems, + NULL); + + fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT, + DATA_TYPE_UINT8_ARRAY, nelems, &counts[0], + NULL); + + for (i = 0; i < nelems; i++) + fm_nvlist_destroy(elems[i], nva ? FM_NVA_RETAIN : FM_NVA_FREE); +} + +/* + * cms_ereport_add_logout entry point + */ +/*ARGSUSED*/ +void +authamd_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *ereport, nv_alloc_t *nva, + int bank, uint64_t status, uint64_t addr, uint64_t misc, + void *mslogout, cms_cookie_t mscookie) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + const authamd_error_disp_t *aed = mscookie; + uint64_t members; + + if (aed == NULL) + return; + + members = aed->aad_ereport_members; + + if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND) { + fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND, + DATA_TYPE_UINT16, (uint16_t)AMD_BANK_SYND(status), + NULL); + + if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { + fm_payload_set(ereport, + FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, + DATA_TYPE_STRING, "E", + NULL); + } + } + + if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND) { + fm_payload_set(ereport, FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND, + DATA_TYPE_UINT16, (uint16_t)AMD_NB_STAT_CKSYND(status), + NULL); + + if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE) { + fm_payload_set(ereport, + FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE, + DATA_TYPE_STRING, "C", + NULL); + } + } + + if (members & FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE && + status & MSR_MC_STATUS_ADDRV) { + authamd_ereport_add_resource(hdl, authamd, ereport, nva, + mslogout); + } +} + +/* + * cms_msrinject entry point + */ +cms_errno_t +authamd_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) +{ + authamd_data_t *authamd = cms_hdl_getcmsdata(hdl); + cms_errno_t rv = CMSERR_BADMSRWRITE; + + authamd_bankstatus_prewrite(hdl, authamd); + if (cmi_hdl_wrmsr(hdl, msr, val) == CMI_SUCCESS) + rv = CMS_SUCCESS; + authamd_bankstatus_postwrite(hdl, authamd); + + return (rv); +} + +cms_api_ver_t _cms_api_version = CMS_API_VERSION_0; + +const cms_ops_t _cms_ops = { + authamd_init, /* cms_init */ + NULL, /* cms_post_startup */ + NULL, /* cms_post_mpstartup */ + authamd_logout_size, /* cms_logout_size */ + authamd_mcgctl_val, /* cms_mcgctl_val */ + authamd_bankctl_skipinit, /* cms_bankctl_skipinit */ + authamd_bankctl_val, /* cms_bankctl_val */ + NULL, /* cms_bankstatus_skipinit */ + NULL, /* cms_bankstatus_val */ + authamd_mca_init, /* cms_mca_init */ + NULL, /* cms_poll_ownermask */ + authamd_bank_logout, /* cms_bank_logout */ + authamd_error_action, /* cms_error_action */ + authamd_disp_match, /* cms_disp_match */ + authamd_ereport_class, /* cms_ereport_class */ + NULL, /* cms_ereport_detector */ + NULL, /* cms_ereport_includestack */ + authamd_ereport_add_logout, /* cms_ereport_add_logout */ + authamd_msrinject, /* cms_msrinject */ + NULL, /* cms_fini */ +}; + +static struct modlcpu modlcpu = { + &mod_cpuops, + "Generic AMD model-specific MCA" +}; + +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.h b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h index f330920591..5e826593d9 100644 --- a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu.h @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -21,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,39 +30,180 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include <sys/types.h> -#include <sys/cpu_module.h> +#include <sys/cpu_module_impl.h> +#include <sys/cpu_module_ms.h> +#include <sys/ksynch.h> +#include <sys/systm.h> +#include <sys/fm/util.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; +#define GCPU_MCA_ERRS_PERCPU 10 /* errorq slots per cpu */ +#define GCPU_MCA_MIN_ERRORS 30 /* minimum total errorq slots */ +#define GCPU_MCA_MAX_ERRORS 100 /* maximum total errorq slots */ -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_data gcpu_data_t; + +#define GCPU_ERRCODE_MASK_ALL 0xffff + +typedef struct gcpu_error_disp { + const char *ged_class_fmt; /* ereport class formatter (last bit) */ + const char *ged_compound_fmt; /* compound error formatter */ + uint64_t ged_ereport_members; /* ereport payload members */ + uint16_t ged_errcode_mask_on; /* errcode bits that must be set ... */ + uint16_t ged_errcode_mask_off; /* ... and must be clear for a match */ +} gcpu_error_disp_t; + +/* + * For errorq_dispatch we need to have a single contiguous structure + * capturing all our logout data. We do not know in advance how many + * error detector banks there are in this cpu model, so we'll manually + * allocate additional space for the gcl_banks array below. + */ +typedef struct gcpu_bank_logout { + uint64_t gbl_status; /* MCi_STATUS value */ + uint64_t gbl_addr; /* MCi_ADDR value */ + uint64_t gbl_misc; /* MCi_MISC value */ + uint64_t gbl_disp; /* Error disposition for this bank */ + uint32_t gbl_clrdefcnt; /* Count of deferred status clears */ +} gcpu_bank_logout_t; + +/* + * The data structure we "logout" all error telemetry from all banks of + * a cpu to. The gcl_data array declared with 1 member below will actually + * have gcl_nbanks members - variable with the actual cpu model present. + * After the gcl_data array there is a further model-specific array that + * may be allocated, and gcl_ms_logout will point to that if present. + * This cpu logout data must form one contiguous chunk of memory for + * dispatch with errorq_dispatch. + */ +typedef struct gcpu_logout { + gcpu_data_t *gcl_gcpu; /* pointer to per-cpu gcpu_data_t */ + uintptr_t gcl_ip; /* instruction pointer from #mc trap */ + uint64_t gcl_timestamp; /* gethrtime() at logout */ + uint64_t gcl_mcg_status; /* MCG_STATUS register value */ + uint64_t gcl_flags; /* Flags */ + pc_t gcl_stack[FM_STK_DEPTH]; /* saved stack trace, if any */ + int gcl_stackdepth; /* saved stack trace depth */ + int gcl_nbanks; /* number of banks in array below */ + void *gcl_ms_logout; /* Model-specific area after gcl_data */ + gcpu_bank_logout_t gcl_data[1]; /* Bank logout areas - must be last */ +} gcpu_logout_t; + +/* + * gcl_flag values + */ +#define GCPU_GCL_F_PRIV 0x1 /* #MC during privileged code */ +#define GCPU_GCL_F_TES_P 0x2 /* MCG_CAP indicates TES_P */ + +struct gcpu_bios_bankcfg { + uint64_t bios_bank_ctl; + uint64_t bios_bank_status; + uint64_t bios_bank_addr; + uint64_t bios_bank_misc; +}; + +struct gcpu_bios_cfg { + uint64_t bios_mcg_cap; + uint64_t bios_mcg_ctl; + struct gcpu_bios_bankcfg *bios_bankcfg; +}; + +#define GCPU_MPT_WHAT_CYC_ERR 0 /* cyclic-induced poll */ +#define GCPU_MPT_WHAT_POKE_ERR 1 /* manually-induced poll */ +#define GCPU_MPT_WHAT_UNFAULTING 2 /* discarded error state */ + +typedef struct gcpu_mca_poll_trace { + hrtime_t mpt_when; /* timestamp of event */ + uint8_t mpt_what; /* GCPU_MPT_WHAT_* (which event?) */ + uint8_t mpt_nerr; /* number of errors discovered */ + uint16_t mpt_pad1; + uint32_t mpt_pad2; +} gcpu_mca_poll_trace_t; + +typedef struct gcpu_mca_poll_trace_ctl { + gcpu_mca_poll_trace_t *mptc_tbufs; /* trace buffers */ + uint_t mptc_curtrace; /* last buffer filled */ +} gcpu_mca_poll_trace_ctl_t; + +/* Index for gcpu_mca_logout array below */ +#define GCPU_MCA_LOGOUT_EXCEPTION 0 /* area for #MC */ +#define GCPU_MCA_LOGOUT_POLLER_1 1 /* next/prev poll area */ +#define GCPU_MCA_LOGOUT_POLLER_2 2 /* prev/next poll area */ +#define GCPU_MCA_LOGOUT_NUM 3 typedef struct gcpu_mca { - const gcpu_mca_bank_t *gcpu_mca_banks; - gcpu_mca_data_t *gcpu_mca_data; + gcpu_logout_t *gcpu_mca_logout[GCPU_MCA_LOGOUT_NUM]; + uint32_t gcpu_mca_nextpoll_idx; /* logout area for next poll */ + struct gcpu_bios_cfg gcpu_mca_bioscfg; uint_t gcpu_mca_nbanks; + uint32_t gcpu_actv_banks; /* MCA banks we initialized */ + size_t gcpu_mca_lgsz; /* size of gcpu_mca_logout structs */ + uint_t gcpu_mca_flags; /* GCPU_MCA_F_* */ + hrtime_t gcpu_mca_lastpoll; + gcpu_mca_poll_trace_ctl_t gcpu_mca_polltrace; } gcpu_mca_t; -typedef struct gcpu_data { - gcpu_mca_t gcpu_mca; -} gcpu_data_t; +typedef struct gcpu_mce_status { + uint_t mce_nerr; /* total errors found in logout of all banks */ + uint64_t mce_disp; /* Disposition information */ + uint_t mce_npcc; /* number of errors with PCC */ + uint_t mce_npcc_ok; /* PCC with CMS_ERRSCOPE_CURCONTEXT_OK */ + uint_t mce_nuc; /* number of errors with UC */ + uint_t mce_nuc_ok; /* UC with CMS_ERRSCOPE_CLEARED_UC */ + uint_t mce_nuc_poisoned; /* UC with CMS_ERRSCOPE_POISONED */ + uint_t mce_forcefatal; /* CMS_ERRSCOPE_FORCE_FATAL */ + uint_t mce_ignored; /* CMS_ERRSCOPE_IGNORE_ERR */ +} gcpu_mce_status_t; + +/* + * Flags for gcpu_mca_flags + */ +#define GCPU_MCA_F_UNFAULTING 0x1 /* CPU exiting faulted state */ + +/* + * State shared by all cpus on a chip + */ +struct gcpu_chipshared { + kmutex_t gcpus_cfglock; /* serial MCA config from chip cores */ + kmutex_t gcpus_poll_lock; /* serialize pollers on the same chip */ + uint32_t gcpus_actv_banks; /* MCA bank numbers active on chip */ +}; + +struct gcpu_data { + gcpu_mca_t gcpu_mca; /* MCA state for this CPU */ + cmi_hdl_t gcpu_hdl; /* associated handle */ + struct gcpu_chipshared *gcpu_shared; /* Shared state for the chip */ +}; + +#ifdef _KERNEL struct regs; -extern void gcpu_mca_init(void *); -extern int gcpu_mca_trap(void *, struct regs *); +/* + * CMI implementation + */ +extern int gcpu_init(cmi_hdl_t, void **); +extern void gcpu_post_startup(cmi_hdl_t); +extern void gcpu_post_mpstartup(cmi_hdl_t); +extern void gcpu_faulted_enter(cmi_hdl_t); +extern void gcpu_faulted_exit(cmi_hdl_t); +extern void gcpu_mca_init(cmi_hdl_t); +extern cmi_errno_t gcpu_msrinject(cmi_hdl_t, cmi_mca_regs_t *, uint_t, int); +extern uint64_t gcpu_mca_trap(cmi_hdl_t, struct regs *); +extern void gcpu_hdl_poke(cmi_hdl_t); + +/* + * Local functions + */ +extern void gcpu_mca_poll_init(cmi_hdl_t); +extern void gcpu_mca_poll_start(cmi_hdl_t); +extern void gcpu_mca_logout(cmi_hdl_t, struct regs *, uint64_t, + gcpu_mce_status_t *, boolean_t); + +#endif /* _KERNEL */ #ifdef __cplusplus } diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c index d8322df273..9cb5bcaff9 100644 --- a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_main.c @@ -39,66 +39,95 @@ #include <sys/cpuvar.h> #include <sys/kmem.h> #include <sys/modctl.h> +#include <sys/pghw.h> #include "gcpu.h" -/*ARGSUSED*/ -static void -gcpu_nop(void *data) -{ -} - -static int -gcpu_notsup(void) -{ - return (ENOTSUP); -} +/* + * Prevent generic cpu support from loading. + */ +int gcpu_disable = 0; -static int -gcpu_nil(void) -{ - return (0); -} +#define GCPU_MAX_CHIPID 32 +static struct gcpu_chipshared *gcpu_shared[GCPU_MAX_CHIPID]; -static void * -gcpu_null(void) +/* + * Our cmi_init entry point, called during startup of each cpu instance. + */ +int +gcpu_init(cmi_hdl_t hdl, void **datap) { - return (NULL); -} + uint_t chipid = cmi_hdl_chipid(hdl); + struct gcpu_chipshared *sp, *osp; + gcpu_data_t *gcpu; + + if (gcpu_disable || chipid >= GCPU_MAX_CHIPID) + return (ENOTSUP); + + /* + * Allocate the state structure for this cpu. We will only + * allocate the bank logout areas in gcpu_mca_init once we + * know how many banks there are. + */ + gcpu = *datap = kmem_zalloc(sizeof (gcpu_data_t), KM_SLEEP); + cmi_hdl_hold(hdl); /* release in gcpu_fini */ + gcpu->gcpu_hdl = hdl; + + /* + * Allocate a chipshared structure if no sibling cpu has already + * allocated it, but allow for the fact that a sibling core may + * be starting up in parallel. + */ + if ((sp = gcpu_shared[chipid]) == NULL) { + sp = kmem_zalloc(sizeof (struct gcpu_chipshared), KM_SLEEP); + osp = atomic_cas_ptr(&gcpu_shared[chipid], NULL, sp); + if (osp == NULL) { + mutex_init(&sp->gcpus_poll_lock, NULL, MUTEX_DRIVER, + NULL); + mutex_init(&sp->gcpus_cfglock, NULL, MUTEX_DRIVER, + NULL); + } else { + kmem_free(sp, sizeof (struct gcpu_chipshared)); + sp = osp; + } + } + gcpu->gcpu_shared = sp; -/*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) +void +gcpu_post_startup(cmi_hdl_t hdl) { - gcpu_data_t *dp = data; + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + + if (gcpu_disable || gcpu == NULL) + return; - kmem_free(dp->gcpu_mca.gcpu_mca_data, - dp->gcpu_mca.gcpu_mca_nbanks * sizeof (gcpu_mca_data_t)); + cms_post_startup(hdl); +} - kmem_free(dp, sizeof (gcpu_data_t)); +void +gcpu_post_mpstartup(cmi_hdl_t hdl) +{ if (!gcpu_disable) { + cms_post_mpstartup(hdl); + gcpu_mca_poll_start(hdl); + } } +cmi_api_ver_t _cmi_api_version = CMI_API_VERSION_1; + const cmi_ops_t _cmi_ops = { - gcpu_init, /* cmi_init */ - gcpu_nop, /* cmi_post_init */ - gcpu_nop, /* cmi_post_mpstartup */ - 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_null /* cmi_mc_getops */ + gcpu_init, /* cmi_init */ + gcpu_post_startup, /* cmi_post_startup */ + gcpu_post_mpstartup, /* cmi_post_mpstartup */ + gcpu_faulted_enter, /* cmi_faulted_enter */ + gcpu_faulted_exit, /* cmi_faulted_exit */ + gcpu_mca_init, /* cmi_mca_init */ + gcpu_mca_trap, /* cmi_mca_trap */ + gcpu_msrinject, /* cmi_msrinject */ + gcpu_hdl_poke, /* cmi_hdl_poke */ + NULL, /* cmi_fini */ }; static struct modlcpu modlcpu = { diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c index 3793ff1087..506cabe337 100644 --- a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_mca.c @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -21,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -29,10 +28,11 @@ #include <sys/mca_x86.h> #include <sys/cpu_module_impl.h> +#include <sys/cpu_module_ms.h> #include <sys/cmn_err.h> #include <sys/cpuvar.h> +#include <sys/pghw.h> #include <sys/x86_archext.h> -#include <sys/controlregs.h> #include <sys/sysmacros.h> #include <sys/regset.h> #include <sys/privregs.h> @@ -40,191 +40,1600 @@ #include <sys/types.h> #include <sys/log.h> #include <sys/psw.h> +#include <sys/fm/protocol.h> +#include <sys/fm/util.h> +#include <sys/errorq.h> +#include <sys/mca_x86.h> +#include <sys/fm/cpu/GMCA.h> +#include <sys/sysevent.h> +#include <sys/ontrap.h> #include "gcpu.h" /* - * x86 architecture standard banks for IA32 and compatible processors. These - * are effectively the lowest common denominators for the MCA architecture. + * gcpu_mca_stack_flag is a debug assist option to capture a stack trace at + * error logout time. The stack will be included in the ereport if the + * error type selects stack inclusion, or in all cases if + * gcpu_mca_stack_ereport_include is nonzero. + */ +int gcpu_mca_stack_flag = 0; +int gcpu_mca_stack_ereport_include = 0; + +/* + * The number of times to re-read MCA telemetry to try to obtain a + * consistent snapshot if we find it to be changing under our feet. */ -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 }, +int gcpu_mca_telemetry_retries = 5; + +static gcpu_error_disp_t gcpu_errtypes[] = { + + /* + * Unclassified + */ + { + FM_EREPORT_CPU_GENERIC_UNCLASSIFIED, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_UNCLASSIFIED_MASKON, + MCAX86_SIMPLE_UNCLASSIFIED_MASKOFF + }, + + /* + * Microcode ROM Parity Error + */ + { + FM_EREPORT_CPU_GENERIC_MC_CODE_PARITY, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_MC_CODE_PARITY_MASKON, + MCAX86_SIMPLE_MC_CODE_PARITY_MASKOFF + }, + + /* + * External - BINIT# from another processor during power-on config + */ + { + FM_EREPORT_CPU_GENERIC_EXTERNAL, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_EXTERNAL_MASKON, + MCAX86_SIMPLE_EXTERNAL_MASKOFF + }, + + /* + * Functional redundancy check master/slave error + */ + { + FM_EREPORT_CPU_GENERIC_FRC, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_FRC_MASKON, + MCAX86_SIMPLE_FRC_MASKOFF + }, + + /* + * Internal timer error + */ + { + FM_EREPORT_CPU_GENERIC_INTERNAL_TIMER, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_INTERNAL_TIMER_MASKON, + MCAX86_SIMPLE_INTERNAL_TIMER_MASKOFF + }, + + /* + * Internal unclassified + */ + { + FM_EREPORT_CPU_GENERIC_INTERNAL_UNCLASS, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKON, + MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKOFF + }, + + /* + * Compound error codes - generic memory hierarchy + */ + { + FM_EREPORT_CPU_GENERIC_GENMEMHIER, + NULL, + FM_EREPORT_PAYLOAD_FLAGS_COMMON, /* yes, no compound name */ + MCAX86_COMPOUND_GENERIC_MEMHIER_MASKON, + MCAX86_COMPOUND_GENERIC_MEMHIER_MASKOFF + }, + + /* + * Compound error codes - TLB errors + */ + { + FM_EREPORT_CPU_GENERIC_TLB, + "%1$s" "TLB" "%2$s" "_ERR", + FM_EREPORT_PAYLOAD_FLAGS_COMPOUND_ERR, + MCAX86_COMPOUND_TLB_MASKON, + MCAX86_COMPOUND_TLB_MASKOFF + }, + + /* + * Compound error codes - memory hierarchy + */ + { + FM_EREPORT_CPU_GENERIC_MEMHIER, + "%1$s" "CACHE" "%2$s" "_" "%3$s" "_ERR", + FM_EREPORT_PAYLOAD_FLAGS_COMPOUND_ERR, + MCAX86_COMPOUND_MEMHIER_MASKON, + MCAX86_COMPOUND_MEMHIER_MASKOFF + }, + + /* + * Compound error codes - bus and interconnect errors + */ + { + FM_EREPORT_CPU_GENERIC_BUS_INTERCONNECT, + "BUS" "%2$s" "_" "%4$s" "_" "%3$s" "_" "%5$s" "_" "%6$s" "_ERR", + FM_EREPORT_PAYLOAD_FLAGS_COMPOUND_ERR, + MCAX86_COMPOUND_BUS_INTERCONNECT_MASKON, + MCAX86_COMPOUND_BUS_INTERCONNECT_MASKOFF + }, }; +static gcpu_error_disp_t gcpu_unknown = { + FM_EREPORT_CPU_GENERIC_UNKNOWN, + "UNKNOWN", + FM_EREPORT_PAYLOAD_FLAGS_COMMON, + 0, + 0 +}; + +static errorq_t *gcpu_mca_queue; +static kmutex_t gcpu_mca_queue_lock; + +static const gcpu_error_disp_t * +gcpu_disp_match(uint16_t code) +{ + const gcpu_error_disp_t *ged = gcpu_errtypes; + int i; + + for (i = 0; i < sizeof (gcpu_errtypes) / sizeof (gcpu_error_disp_t); + i++, ged++) { + uint16_t on = ged->ged_errcode_mask_on; + uint16_t off = ged->ged_errcode_mask_off; + + if ((code & on) == on && (code & off) == 0) + return (ged); + } + + return (NULL); +} + +static uint8_t +bit_strip(uint16_t code, uint16_t mask, uint16_t shift) +{ + return ((uint8_t)(code & mask) >> shift); +} + +#define BIT_STRIP(code, name) \ + bit_strip(code, MCAX86_ERRCODE_##name##_MASK, \ + MCAX86_ERRCODE_##name##_SHIFT) + +#define GCPU_MNEMONIC_UNDEF "undefined" +#define GCPU_MNEMONIC_RESVD "reserved" + /* - * 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). + * Mappings of TT, LL, RRRR, PP, II and T values to compound error name + * mnemonics and to ereport class name components. */ -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 }, + +struct gcpu_mnexp { + const char *mne_compound; /* used in expanding compound errname */ + const char *mne_ereport; /* used in expanding ereport class */ +}; + +static struct gcpu_mnexp gcpu_TT_mnemonics[] = { /* MCAX86_ERRCODE_TT_* */ + { "I", FM_EREPORT_CPU_GENERIC_TT_INSTR }, /* INSTR */ + { "D", FM_EREPORT_CPU_GENERIC_TT_DATA }, /* DATA */ + { "G", FM_EREPORT_CPU_GENERIC_TT_GEN }, /* GEN */ + { GCPU_MNEMONIC_UNDEF, "" } +}; + +static struct gcpu_mnexp gcpu_LL_mnemonics[] = { /* MCAX86_ERRCODE_LL_* */ + { "LO", FM_EREPORT_CPU_GENERIC_LL_L0 }, /* L0 */ + { "L1", FM_EREPORT_CPU_GENERIC_LL_L1 }, /* L1 */ + { "L2", FM_EREPORT_CPU_GENERIC_LL_L2 }, /* L2 */ + { "LG", FM_EREPORT_CPU_GENERIC_LL_LG } /* LG */ }; +static struct gcpu_mnexp gcpu_RRRR_mnemonics[] = { /* MCAX86_ERRCODE_RRRR_* */ + { "ERR", FM_EREPORT_CPU_GENERIC_RRRR_ERR }, /* ERR */ + { "RD", FM_EREPORT_CPU_GENERIC_RRRR_RD }, /* RD */ + { "WR", FM_EREPORT_CPU_GENERIC_RRRR_WR }, /* WR */ + { "DRD", FM_EREPORT_CPU_GENERIC_RRRR_DRD }, /* DRD */ + { "DWR", FM_EREPORT_CPU_GENERIC_RRRR_DWR }, /* DWR */ + { "IRD", FM_EREPORT_CPU_GENERIC_RRRR_IRD }, /* IRD */ + { "PREFETCH", FM_EREPORT_CPU_GENERIC_RRRR_PREFETCH }, /* PREFETCH */ + { "EVICT", FM_EREPORT_CPU_GENERIC_RRRR_EVICT }, /* EVICT */ + { "SNOOP", FM_EREPORT_CPU_GENERIC_RRRR_SNOOP }, /* SNOOP */ +}; + +static struct gcpu_mnexp gcpu_PP_mnemonics[] = { /* MCAX86_ERRCODE_PP_* */ + { "SRC", FM_EREPORT_CPU_GENERIC_PP_SRC }, /* SRC */ + { "RES", FM_EREPORT_CPU_GENERIC_PP_RES }, /* RES */ + { "OBS", FM_EREPORT_CPU_GENERIC_PP_OBS }, /* OBS */ + { "", FM_EREPORT_CPU_GENERIC_PP_GEN } /* GEN */ +}; + +static struct gcpu_mnexp gcpu_II_mnemonics[] = { /* MCAX86_ERRCODE_II_* */ + { "M", FM_EREPORT_CPU_GENERIC_II_MEM }, /* MEM */ + { GCPU_MNEMONIC_RESVD, "" }, + { "IO", FM_EREPORT_CPU_GENERIC_II_IO }, /* IO */ + { "", FM_EREPORT_CPU_GENERIC_II_GEN } /* GEN */ +}; + +static struct gcpu_mnexp gcpu_T_mnemonics[] = { /* MCAX86_ERRCODE_T_* */ + { "NOTIMEOUT", FM_EREPORT_CPU_GENERIC_T_NOTIMEOUT }, /* NONE */ + { "TIMEOUT", FM_EREPORT_CPU_GENERIC_T_TIMEOUT } /* TIMEOUT */ +}; + +enum gcpu_mn_namespace { + GCPU_MN_NAMESPACE_COMPOUND, + GCPU_MN_NAMESPACE_EREPORT +}; + +static const char * +gcpu_mnemonic(const struct gcpu_mnexp *tbl, size_t tbl_sz, uint8_t val, + enum gcpu_mn_namespace nspace) +{ + if (val >= tbl_sz) + return (GCPU_MNEMONIC_UNDEF); /* for all namespaces */ + + switch (nspace) { + case GCPU_MN_NAMESPACE_COMPOUND: + return (tbl[val].mne_compound); + /*NOTREACHED*/ + + case GCPU_MN_NAMESPACE_EREPORT: + return (tbl[val].mne_ereport); + /*NOTREACHED*/ + + default: + return (GCPU_MNEMONIC_UNDEF); + /*NOTREACHED*/ + } +} + /* - * 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. + * The ereport class leaf component is either a simple string with no + * format specifiers, or a string with one or more embedded %n$s specifiers - + * positional selection for string arguments. The kernel snprintf does + * not support %n$ (and teaching it to do so is too big a headache) so + * we will expand this restricted format string ourselves. */ + +#define GCPU_CLASS_VARCOMPS 7 + +#define GCPU_MNEMONIC(code, name, nspace) \ + gcpu_mnemonic(gcpu_##name##_mnemonics, \ + sizeof (gcpu_##name##_mnemonics) / sizeof (struct gcpu_mnexp), \ + BIT_STRIP(code, name), nspace) + +static void +gcpu_mn_fmt(const char *fmt, char *buf, size_t buflen, uint64_t status, + enum gcpu_mn_namespace nspace) +{ + uint16_t code = MCAX86_ERRCODE(status); + const char *mn[GCPU_CLASS_VARCOMPS]; + char *p = buf; /* current position in buf */ + char *q = buf + buflen; /* pointer past last char in buf */ + int which, expfmtchar, error; + char c; + + mn[0] = GCPU_MNEMONIC(code, TT, nspace); + mn[1] = GCPU_MNEMONIC(code, LL, nspace); + mn[2] = GCPU_MNEMONIC(code, RRRR, nspace); + mn[3] = GCPU_MNEMONIC(code, PP, nspace); + mn[4] = GCPU_MNEMONIC(code, II, nspace); + mn[5] = GCPU_MNEMONIC(code, T, nspace); + mn[6] = (status & MSR_MC_STATUS_UC) ? "_uc" : ""; + + while (p < q - 1 && (c = *fmt++) != '\0') { + if (c != '%') { + /* not the beginning of a format specifier - copy */ + *p++ = c; + continue; + } + + error = 0; + which = -1; + expfmtchar = -1; + +nextfmt: + if ((c = *fmt++) == '\0') + break; /* early termination of fmt specifier */ + + switch (c) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + if (which != -1) { /* allow only one positional digit */ + error++; + break; + } + which = c - '1'; + goto nextfmt; + /*NOTREACHED*/ + + case '$': + if (which == -1) { /* no position specified */ + error++; + break; + } + expfmtchar = 's'; + goto nextfmt; + /*NOTREACHED*/ + + case 's': + if (expfmtchar != 's') { + error++; + break; + } + (void) snprintf(p, (uintptr_t)q - (uintptr_t)p, "%s", + mn[which]); + p += strlen(p); + break; + + default: + error++; + break; + } + + if (error) + break; + } + + *p = '\0'; /* NUL termination */ +} + +static void +gcpu_erpt_clsfmt(const char *fmt, char *buf, size_t buflen, uint64_t status, + const char *cpuclass, const char *leafclass) +{ + char *p = buf; /* current position in buf */ + char *q = buf + buflen; /* pointer past last char in buf */ + + (void) snprintf(buf, (uintptr_t)q - (uintptr_t)p, "%s.%s.", + FM_ERROR_CPU, cpuclass ? cpuclass : FM_EREPORT_CPU_GENERIC); + + p += strlen(p); + if (p >= q) + return; + + if (leafclass == NULL) { + gcpu_mn_fmt(fmt, p, (uintptr_t)q - (uintptr_t)p, status, + GCPU_MN_NAMESPACE_EREPORT); + } else { + (void) snprintf(p, (uintptr_t)q - (uintptr_t)p, "%s", + leafclass); + } +} + +/* + * Create an "hc" scheme FMRI identifying the given cpu. We don't know + * the actual topology/connectivity of cpus in the system, so we'll + * apply /motherboard=0/chip=.../cpu=... in all cases. + */ +static nvlist_t * +gcpu_fmri_create(cmi_hdl_t hdl, nv_alloc_t *nva) +{ + nvlist_t *nvl; + + if ((nvl = fm_nvlist_create(nva)) == NULL) + return (NULL); + + fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3, + "motherboard", 0, + "chip", cmi_hdl_chipid(hdl), + "cpu", cmi_hdl_coreid(hdl)); + + return (nvl); +} + +int gcpu_bleat_count_thresh = 5; +hrtime_t gcpu_bleat_min_interval = 10 * 1000000000ULL; + +/* + * Called when we are unable to propogate a logout structure onto an + * errorq for subsequent ereport preparation and logging etc. The caller + * should usually only decide to call this for severe errors - those we + * suspect we may need to panic for. + */ +static void +gcpu_bleat(cmi_hdl_t hdl, gcpu_logout_t *gcl) +{ + hrtime_t now = gethrtime_waitfree(); + static hrtime_t gcpu_last_bleat; + gcpu_bank_logout_t *gbl; + static int bleatcount; + int i; + + /* + * Throttle spamming of the console. The first gcpu_bleat_count_thresh + * can come as fast as we like, but once we've spammed that many + * to the console we require a minimum interval to pass before + * any more complaints. + */ + if (++bleatcount > gcpu_bleat_count_thresh) { + if (now - gcpu_last_bleat < gcpu_bleat_min_interval) + return; + else + bleatcount = 0; + } + gcpu_last_bleat = now; + + cmn_err(CE_WARN, "Machine-Check Errors unlogged on chip %d core %d, " + "raw dump follows", cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl)); + cmn_err(CE_WARN, "MCG_STATUS 0x%016llx", + (u_longlong_t)gcl->gcl_mcg_status); + for (i = 0, gbl = &gcl->gcl_data[0]; i < gcl->gcl_nbanks; i++, gbl++) { + uint64_t status = gbl->gbl_status; + + if (!(status & MSR_MC_STATUS_VAL)) + continue; + + switch (status & (MSR_MC_STATUS_ADDRV | MSR_MC_STATUS_MISCV)) { + case MSR_MC_STATUS_ADDRV | MSR_MC_STATUS_MISCV: + cmn_err(CE_WARN, "Bank %d (offset 0x%llx) " + "STAT 0x%016llx ADDR 0x%016llx MISC 0x%016llx", + i, IA32_MSR_MC(i, STATUS), + (u_longlong_t)status, + (u_longlong_t)gbl->gbl_addr, + (u_longlong_t)gbl->gbl_misc); + break; + + case MSR_MC_STATUS_ADDRV: + cmn_err(CE_WARN, "Bank %d (offset 0x%llx) " + "STAT 0x%016llx ADDR 0x%016llx", + i, IA32_MSR_MC(i, STATUS), + (u_longlong_t)status, + (u_longlong_t)gbl->gbl_addr); + break; + + case MSR_MC_STATUS_MISCV: + cmn_err(CE_WARN, "Bank %d (offset 0x%llx) " + "STAT 0x%016llx MISC 0x%016llx", + i, IA32_MSR_MC(i, STATUS), + (u_longlong_t)status, + (u_longlong_t)gbl->gbl_misc); + break; + + default: + cmn_err(CE_WARN, "Bank %d (offset 0x%llx) " + "STAT 0x%016llx", + i, IA32_MSR_MC(i, STATUS), + (u_longlong_t)status); + break; + + } + } +} + +#define _GCPU_BSTATUS(status, what) \ + FM_EREPORT_PAYLOAD_NAME_MC_STATUS_##what, DATA_TYPE_BOOLEAN_VALUE, \ + (status) & MSR_MC_STATUS_##what ? B_TRUE : B_FALSE + +static void +gcpu_ereport_add_logout(nvlist_t *ereport, const gcpu_logout_t *gcl, + uint_t bankno, const gcpu_error_disp_t *ged, uint16_t code) +{ + uint64_t members = ged ? ged->ged_ereport_members : + FM_EREPORT_PAYLOAD_FLAGS_COMMON; + uint64_t mcg = gcl->gcl_mcg_status; + int mcip = mcg & MCG_STATUS_MCIP; + const gcpu_bank_logout_t *gbl = &gcl->gcl_data[bankno]; + uint64_t bstat = gbl->gbl_status; + + /* + * Include the compound error name if requested and if this + * is a compound error type. + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_COMPOUND_ERR && ged && + ged->ged_compound_fmt != NULL) { + char buf[FM_MAX_CLASS]; + + gcpu_mn_fmt(ged->ged_compound_fmt, buf, sizeof (buf), code, + GCPU_MN_NAMESPACE_COMPOUND); + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_COMPOUND_ERR, + DATA_TYPE_STRING, buf, NULL); + } + + /* + * Include disposition information for this error + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_DISP && + gbl->gbl_disp != 0) { + int i, empty = 1; + char buf[128]; + char *p = buf, *q = buf + 128; + static struct _gcpu_disp_name { + uint64_t dv; + const char *dn; + } disp_names[] = { + { CMI_ERRDISP_CURCTXBAD, + "processor_context_corrupt" }, + { CMI_ERRDISP_RIPV_INVALID, + "return_ip_invalid" }, + { CMI_ERRDISP_UC_UNCONSTRAINED, + "unconstrained" }, + { CMI_ERRDISP_FORCEFATAL, + "forcefatal" }, + { CMI_ERRDISP_IGNORED, + "ignored" }, + { CMI_ERRDISP_PCC_CLEARED, + "corrupt_context_cleared" }, + { CMI_ERRDISP_UC_CLEARED, + "uncorrected_data_cleared" }, + { CMI_ERRDISP_POISONED, + "poisoned" }, + { CMI_ERRDISP_INCONSISTENT, + "telemetry_unstable" }, + }; + + for (i = 0; i < sizeof (disp_names) / + sizeof (struct _gcpu_disp_name); i++) { + if ((gbl->gbl_disp & disp_names[i].dv) == 0) + continue; + + (void) snprintf(p, (uintptr_t)q - (uintptr_t)p, + "%s%s", empty ? "" : ",", disp_names[i].dn); + p += strlen(p); + empty = 0; + } + + if (p != buf) + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_DISP, + DATA_TYPE_STRING, buf, NULL); + } + + /* + * If MCG_STATUS is included add that and an indication of whether + * this ereport was the result of a machine check or poll. + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_MCG_STATUS) { + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_MCG_STATUS, + DATA_TYPE_UINT64, mcg, NULL); + + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_MCG_STATUS_MCIP, + DATA_TYPE_BOOLEAN_VALUE, mcip ? B_TRUE : B_FALSE, NULL); + } + + /* + * If an instruction pointer is to be included add one provided + * MCG_STATUS indicated it is valid; meaningless for polled events. + */ + if (mcip && members & FM_EREPORT_PAYLOAD_FLAG_IP && + mcg & MCG_STATUS_EIPV) { + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_IP, + DATA_TYPE_UINT64, gcl->gcl_ip, NULL); + } + + /* + * Add an indication of whether the trap occured during privileged code. + */ + if (mcip && members & FM_EREPORT_PAYLOAD_FLAG_PRIV) { + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_PRIV, + DATA_TYPE_BOOLEAN_VALUE, + gcl->gcl_flags & GCPU_GCL_F_PRIV ? B_TRUE : B_FALSE, NULL); + } + + /* + * If requested, add the index of the MCA bank. This indicates the + * n'th bank of 4 MCA registers, and does not necessarily correspond + * to MCi_* - use the bank offset to correlate + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_BANK_NUM) { + fm_payload_set(ereport, + /* Bank number */ + FM_EREPORT_PAYLOAD_NAME_BANK_NUM, DATA_TYPE_UINT8, bankno, + /* Offset of MCi_CTL */ + FM_EREPORT_PAYLOAD_NAME_BANK_MSR_OFFSET, DATA_TYPE_UINT64, + IA32_MSR_MC(bankno, CTL), + NULL); + } + + /* + * Add MCi_STATUS if requested, and decode it. + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_MC_STATUS) { + const char *tbes[] = { + "No tracking", /* 00 */ + "Green - below threshold", /* 01 */ + "Yellow - above threshold", /* 10 */ + "Reserved" /* 11 */ + }; + + fm_payload_set(ereport, + /* Bank MCi_STATUS */ + FM_EREPORT_PAYLOAD_NAME_MC_STATUS, DATA_TYPE_UINT64, bstat, + /* Overflow? */ + _GCPU_BSTATUS(bstat, OVER), + /* Uncorrected? */ + _GCPU_BSTATUS(bstat, UC), + /* Enabled? */ + _GCPU_BSTATUS(bstat, EN), + /* Processor context corrupt? */ + _GCPU_BSTATUS(bstat, PCC), + /* Error code */ + FM_EREPORT_PAYLOAD_NAME_MC_STATUS_ERRCODE, + DATA_TYPE_UINT16, MCAX86_ERRCODE(bstat), + /* Model-specific error code */ + FM_EREPORT_PAYLOAD_NAME_MC_STATUS_EXTERRCODE, + DATA_TYPE_UINT16, MCAX86_MSERRCODE(bstat), + NULL); + + /* + * If MCG_CAP.TES_P indicates that that thresholding info + * is present in the architural component of the bank status + * then include threshold information for this bank. + */ + if (gcl->gcl_flags & GCPU_GCL_F_TES_P) { + fm_payload_set(ereport, + FM_EREPORT_PAYLOAD_NAME_MC_STATUS_TES, + DATA_TYPE_STRING, tbes[MCAX86_TBES_VALUE(bstat)], + NULL); + } + } + + /* + * MCi_ADDR info if requested and valid. + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_MC_ADDR && + bstat & MSR_MC_STATUS_ADDRV) { + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_MC_ADDR, + DATA_TYPE_UINT64, gbl->gbl_addr, NULL); + } + + /* + * MCi_MISC if requested and MCi_STATUS.MISCV). + */ + if (members & FM_EREPORT_PAYLOAD_FLAG_MC_MISC && + bstat & MSR_MC_STATUS_MISCV) { + fm_payload_set(ereport, FM_EREPORT_PAYLOAD_NAME_MC_MISC, + DATA_TYPE_UINT64, gbl->gbl_misc, NULL); + } + +} + +/* + * Construct and post an ereport based on the logout information from a + * single MCA bank. We are not necessarily running on the cpu that + * detected the error. + */ +static void +gcpu_ereport_post(const gcpu_logout_t *gcl, int bankidx, + const gcpu_error_disp_t *ged, cms_cookie_t mscookie, uint64_t status) +{ + gcpu_data_t *gcpu = gcl->gcl_gcpu; + cmi_hdl_t hdl = gcpu->gcpu_hdl; + const gcpu_bank_logout_t *gbl = &gcl->gcl_data[bankidx]; + const char *cpuclass = NULL, *leafclass = NULL; + uint16_t code = MCAX86_ERRCODE(status); + errorq_elem_t *eqep, *scr_eqep; + nvlist_t *ereport, *detector; + char buf[FM_MAX_CLASS]; + const char *classfmt; + nv_alloc_t *nva; + + if (panicstr) { + if ((eqep = errorq_reserve(ereport_errorq)) == NULL) + return; + ereport = errorq_elem_nvl(ereport_errorq, eqep); + + /* + * Allocate another element for scratch space, but fallback + * to the one we have if that fails. We'd like to use the + * additional scratch space for nvlist construction. + */ + if ((scr_eqep = errorq_reserve(ereport_errorq)) != NULL) + nva = errorq_elem_nva(ereport_errorq, scr_eqep); + else + nva = errorq_elem_nva(ereport_errorq, eqep); + } else { + ereport = fm_nvlist_create(NULL); + nva = NULL; + } + + if (ereport == NULL) + return; + + /* + * Common payload data required by the protocol: + * - ereport class + * - detector + * - ENA + */ + + /* + * Ereport class - call into model-specific support to allow it to + * provide a cpu class or leaf class, otherwise calculate our own. + */ + cms_ereport_class(hdl, mscookie, &cpuclass, &leafclass); + classfmt = ged ? ged->ged_class_fmt : FM_EREPORT_CPU_GENERIC_UNKNOWN; + gcpu_erpt_clsfmt(classfmt, buf, sizeof (buf), status, cpuclass, + leafclass); + + /* + * The detector FMRI. + */ + if ((detector = cms_ereport_detector(hdl, mscookie, nva)) == NULL) + detector = gcpu_fmri_create(hdl, nva); + + /* + * Should we define a new ENA format 3?? for chip/core/strand? + * It will be better when virtualized. + */ + fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, + fm_ena_generate_cpu(gcl->gcl_timestamp, + cmi_hdl_chipid(hdl) << 6 | cmi_hdl_coreid(hdl) << 3 | + cmi_hdl_strandid(hdl), FM_ENA_FMT1), detector, NULL); + + if (panicstr) { + fm_nvlist_destroy(detector, FM_NVA_RETAIN); + nv_alloc_reset(nva); + } else { + fm_nvlist_destroy(detector, FM_NVA_FREE); + } + + /* + * Add the architectural ereport class-specific payload data. + */ + gcpu_ereport_add_logout(ereport, gcl, bankidx, ged, code); + + /* + * Allow model-specific code to add ereport members. + */ + cms_ereport_add_logout(hdl, ereport, nva, bankidx, gbl->gbl_status, + gbl->gbl_addr, gbl->gbl_misc, gcl->gcl_ms_logout, mscookie); + + /* + * Include stack if options is turned on and either selected in + * the payload member bitmask or inclusion is forced. + */ + if (gcpu_mca_stack_flag && + (cms_ereport_includestack(hdl, mscookie) == + B_TRUE || gcpu_mca_stack_ereport_include)) { + fm_payload_stack_add(ereport, gcl->gcl_stack, + gcl->gcl_stackdepth); + } + + /* + * Post ereport. + */ + if (panicstr) { + errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); + if (scr_eqep) + errorq_cancel(ereport_errorq, scr_eqep); + } else { + (void) fm_ereport_post(ereport, EVCH_TRYHARD); + fm_nvlist_destroy(ereport, FM_NVA_FREE); + } + +} + +/*ARGSUSED*/ void -gcpu_mca_init(void *data) +gcpu_mca_drain(void *ignored, const void *data, const errorq_elem_t *eqe) { - gcpu_data_t *gcpu = data; - gcpu_mca_t *mca = &gcpu->gcpu_mca; - cpu_t *cp = CPU; + const gcpu_logout_t *gcl = data; + const gcpu_bank_logout_t *gbl; + int i; + + for (i = 0, gbl = &gcl->gcl_data[0]; i < gcl->gcl_nbanks; i++, gbl++) { + const gcpu_error_disp_t *gened; + cms_cookie_t mscookie; + + if (gbl->gbl_status & MSR_MC_STATUS_VAL && + !(gbl->gbl_disp & CMI_ERRDISP_INCONSISTENT)) { + uint16_t code = MCAX86_ERRCODE(gbl->gbl_status); + + /* + * Perform a match based on IA32 MCA architectural + * components alone. + */ + gened = gcpu_disp_match(code); /* may be NULL */ + + /* + * Now see if an model-specific match can be made. + */ + mscookie = cms_disp_match(gcl->gcl_gcpu->gcpu_hdl, i, + gbl->gbl_status, gbl->gbl_addr, gbl->gbl_misc, + gcl->gcl_ms_logout); + + /* + * Prepare and dispatch an ereport for logging and + * diagnosis. + */ + gcpu_ereport_post(gcl, i, gened, mscookie, + gbl->gbl_status); + } else if (gbl->gbl_status & MSR_MC_STATUS_VAL && + (gbl->gbl_disp & CMI_ERRDISP_INCONSISTENT)) { + /* + * Telemetry kept changing as we tried to read + * it. Force an unknown ereport leafclass but + * keep the telemetry unchanged for logging. + */ + gcpu_ereport_post(gcl, i, &gcpu_unknown, NULL, + gbl->gbl_status); + } + } +} + +static size_t gcpu_mca_queue_datasz = 0; + +/* + * The following code is ready to make a weak attempt at growing the + * errorq structure size. Since it is not foolproof (we don't know + * who may already be producing to the outgoing errorq) our caller + * instead assures that we'll always be called with no greater data + * size than on our first call. + */ +static void +gcpu_errorq_init(size_t datasz) +{ + int slots; + + mutex_enter(&gcpu_mca_queue_lock); + if (gcpu_mca_queue_datasz >= datasz) { + mutex_exit(&gcpu_mca_queue_lock); + return; + } + + membar_producer(); + if (gcpu_mca_queue) { + gcpu_mca_queue_datasz = 0; + errorq_destroy(gcpu_mca_queue); + } + + slots = MAX(GCPU_MCA_ERRS_PERCPU * max_ncpus, GCPU_MCA_MIN_ERRORS); + slots = MIN(slots, GCPU_MCA_MAX_ERRORS); + + gcpu_mca_queue = errorq_create("gcpu_mca_queue", gcpu_mca_drain, + NULL, slots, datasz, 1, ERRORQ_VITAL); + + if (gcpu_mca_queue != NULL) + gcpu_mca_queue_datasz = datasz; + + mutex_exit(&gcpu_mca_queue_lock); +} + +/* + * Perform MCA initialization as described in section 14.6 of Intel 64 + * and IA-32 Architectures Software Developer's Manual Volume 3A. + */ + +static uint_t global_nbanks; + +void +gcpu_mca_init(cmi_hdl_t hdl) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); uint64_t cap; + uint_t vendor = cmi_hdl_vendor(hdl); + uint_t family = cmi_hdl_family(hdl); + gcpu_mca_t *mca = &gcpu->gcpu_mca; + int mcg_ctl_present; uint_t nbanks; + size_t mslsz; int i; + if (gcpu == NULL) + return; + + /* + * Protect from some silly /etc/system settings. + */ + if (gcpu_mca_telemetry_retries < 0 || gcpu_mca_telemetry_retries > 100) + gcpu_mca_telemetry_retries = 5; + + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != CMI_SUCCESS) + return; + /* - * 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. + * CPU startup code only calls cmi_mca_init if x86_feature indicates + * both MCA and MCE support (i.e., X86_MCA). P5, K6, and earlier + * processors, which have their own * more primitive way of doing + * machine checks, will not have cmi_mca_init called since their + * CPUID information will not indicate both MCA and MCE features. */ +#ifndef __xpv ASSERT(x86_feature & X86_MCA); - cap = rdmsr(IA32_MSR_MCG_CAP); +#endif /* __xpv */ - if (!(cap & MCG_CAP_CTL_P)) - return; /* do nothing if IA32_MCG_CTL register is missing */ + /* + * Determine whether the IA32_MCG_CTL register is present. If it + * is we will enable all features by writing -1 to it towards + * the end of this initialization; if it is absent then volume 3A + * says we must nonetheless continue to initialize the individual + * banks. + */ + mcg_ctl_present = cap & MCG_CAP_CTL_P; + + /* + * We squirell values away for inspection/debugging. + */ + mca->gcpu_mca_bioscfg.bios_mcg_cap = cap; + if (mcg_ctl_present) + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CTL, + &mca->gcpu_mca_bioscfg.bios_mcg_ctl); + + /* + * Determine the number of error-reporting banks implemented. + */ + mca->gcpu_mca_nbanks = nbanks = cap & MCG_CAP_COUNT_MASK; + + if (nbanks != 0 && global_nbanks == 0) + global_nbanks = nbanks; /* no race - BSP will get here first */ + + /* + * If someone is hiding the number of banks (perhaps we are fully + * virtualized?) or if this processor has more banks than the + * first to set global_nbanks then bail. The latter requirement + * is because we need to size our errorq data structure and we + * don't want to have to grow the errorq (destroy and recreate) + * which may just lose some telemetry. + */ + if (nbanks == 0 || nbanks > global_nbanks) + return; + + mca->gcpu_mca_bioscfg.bios_bankcfg = kmem_zalloc(nbanks * + sizeof (struct gcpu_bios_bankcfg), KM_SLEEP); + + /* + * Calculate the size we need to allocate for a gcpu_logout_t + * with a gcl_data array big enough for all banks of this cpu. + * Add any space requested by the model-specific logout support. + */ + mslsz = cms_logout_size(hdl); + mca->gcpu_mca_lgsz = sizeof (gcpu_logout_t) + + (nbanks - 1) * sizeof (gcpu_bank_logout_t) + mslsz; + + for (i = 0; i < GCPU_MCA_LOGOUT_NUM; i++) { + gcpu_logout_t *gcl; + + mca->gcpu_mca_logout[i] = gcl = + kmem_zalloc(mca->gcpu_mca_lgsz, KM_SLEEP); + gcl->gcl_gcpu = gcpu; + gcl->gcl_nbanks = nbanks; + gcl->gcl_ms_logout = (mslsz == 0) ? NULL : + (char *)(&gcl->gcl_data[0]) + nbanks * + sizeof (gcpu_bank_logout_t); - 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_nextpoll_idx = GCPU_MCA_LOGOUT_POLLER_1; + + /* + * Create our errorq to transport the logout structures. This + * can fail so users of gcpu_mca_queue must be prepared for NULL. + */ + gcpu_errorq_init(mca->gcpu_mca_lgsz); - mca->gcpu_mca_data = kmem_alloc( - mca->gcpu_mca_nbanks * sizeof (gcpu_mca_data_t), KM_SLEEP); + /* + * Not knowing which, if any, banks are shared between cores we + * assure serialization of MCA bank initialization by each cpu + * on the chip. On chip architectures in which some banks are + * shared this will mean the shared resource is initialized more + * than once - we're simply aiming to avoid simultaneous MSR writes + * to the shared resource. + * + * Even with these precautions, some platforms may yield a GP fault + * if a core other than a designated master tries to write anything + * but all 0's to MCi_{STATUS,ADDR,CTL}. So we will perform + * those writes under on_trap protection. + */ + mutex_enter(&gcpu->gcpu_shared->gcpus_cfglock); /* - * 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. + * Initialize poller data, but don't start polling yet. */ - 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 */ + gcpu_mca_poll_init(hdl); + + /* + * Work out which MCA banks we will initialize. In MCA logout + * code we will only read those banks which we initialize here. + */ + for (i = 0; i < nbanks; i++) { + /* + * On Intel family 6 and AMD family 6 we must not enable + * machine check from bank 0 detectors. In the Intel + * case bank 0 is reserved for the platform, while in the + * AMD case reports are that enabling bank 0 (DC) produces + * spurious machine checks. + */ + if (i == 0 && ((vendor == X86_VENDOR_Intel || + vendor == X86_VENDOR_AMD) && family == 6)) + continue; - 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); + if (cms_bankctl_skipinit(hdl, i)) + continue; + + /* + * Record which MCA banks were enabled, both from the + * point of view of this core and accumulating for the + * whole chip (if some cores share a bank we must be + * sure either can logout from it). + */ + mca->gcpu_actv_banks |= 1 << i; + atomic_or_32(&gcpu->gcpu_shared->gcpus_actv_banks, 1 << i); } - wrmsr(IA32_MSR_MCG_CTL, -1ULL); /* enable all machine-check features */ - setcr4(getcr4() | CR4_MCE); /* enable machine-check exceptions */ + /* + * Log any valid telemetry lurking in the MCA banks, but do not + * clear the status registers. Ignore the disposition returned - + * we have already paniced or reset for any nasty errors found here. + */ + gcpu_mca_logout(hdl, NULL, -1ULL, NULL, B_FALSE); + + /* + * Initialize all MCi_CTL and clear all MCi_STATUS, allowing the + * model-specific module the power of veto. + */ + for (i = 0; i < nbanks; i++) { + struct gcpu_bios_bankcfg *bcfgp = + mca->gcpu_mca_bioscfg.bios_bankcfg + i; + + /* + * Stash inherited bank MCA state, even for banks we will + * not initialize ourselves. Do not read the MISC register + * unconditionally - on some processors that will #GP on + * banks that do not implement the MISC register (would be + * caught by on_trap, anyway). + */ + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, CTL), + &bcfgp->bios_bank_ctl); + + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, STATUS), + &bcfgp->bios_bank_status); + + if (bcfgp->bios_bank_status & MSR_MC_STATUS_ADDRV) + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, ADDR), + &bcfgp->bios_bank_addr); + + if (bcfgp->bios_bank_status & MSR_MC_STATUS_MISCV) + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, MISC), + &bcfgp->bios_bank_misc); + + if (!(mca->gcpu_actv_banks & 1 << i)) + continue; + + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MC(i, CTL), + cms_bankctl_val(hdl, i, -1ULL)); + + if (!cms_bankstatus_skipinit(hdl, i)) { + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MC(i, STATUS), + cms_bankstatus_val(hdl, i, 0ULL)); + } + } + + /* + * Now let the model-specific support perform further initialization + * of non-architectural features. + */ + cms_mca_init(hdl, nbanks); + + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MCG_STATUS, 0ULL); + membar_producer(); + + /* enable all machine-check features */ + if (mcg_ctl_present) + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MCG_CTL, + cms_mcgctl_val(hdl, nbanks, -1ULL)); + + mutex_exit(&gcpu->gcpu_shared->gcpus_cfglock); + + /* enable machine-check exception in CR4 */ + cmi_hdl_enable_mce(hdl); } -/* - * 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) +static uint64_t +gcpu_mca_process(cmi_hdl_t hdl, struct regs *rp, int nerr, gcpu_data_t *gcpu, + gcpu_logout_t *gcl, int ismc, gcpu_mce_status_t *mcesp) { - gcpu_data_t *gcpu = data; + int curctxbad = 0, unconstrained = 0, forcefatal = 0; gcpu_mca_t *mca = &gcpu->gcpu_mca; - uint64_t gstatus = rdmsr(IA32_MSR_MCG_STATUS); - int i, fatal = !(gstatus & MCG_STATUS_RIPV); + int nbanks = mca->gcpu_mca_nbanks; + gcpu_mce_status_t mce; + gcpu_bank_logout_t *gbl; + uint64_t disp = 0; + int i; + + if (mcesp == NULL) + mcesp = &mce; - if (!(gstatus & MCG_STATUS_MCIP)) - return (0); /* spurious machine check trap */ + mcesp->mce_nerr = nerr; + + mcesp->mce_npcc = mcesp->mce_npcc_ok = mcesp->mce_nuc = + mcesp->mce_nuc_ok = mcesp->mce_nuc_poisoned = + mcesp->mce_forcefatal = mcesp->mce_ignored = 0; /* - * 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. + * If this a machine check then if the return instruction pointer + * is not valid the current context is lost. */ - 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); + if (ismc && !(gcl->gcl_mcg_status & MCG_STATUS_RIPV)) + disp |= CMI_ERRDISP_RIPV_INVALID; + + for (i = 0, gbl = &gcl->gcl_data[0]; i < nbanks; i++, gbl++) { + uint64_t mcistatus = gbl->gbl_status; + uint32_t ms_scope; + int pcc, uc; + int poisoned; - data->bank_status_data = bstatus; - data->bank_addr_data = 0; - data->bank_misc_data = 0; + if (!(mcistatus & MSR_MC_STATUS_VAL)) + continue; - if (!(bstatus & MSR_MC_STATUS_VAL)) + if (gbl->gbl_disp & CMI_ERRDISP_INCONSISTENT) 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); + pcc = (mcistatus & MSR_MC_STATUS_PCC) != 0; + uc = (mcistatus & MSR_MC_STATUS_UC) != 0; + mcesp->mce_npcc += pcc; + mcesp->mce_nuc += uc; - if (bstatus & (MSR_MC_STATUS_PCC | MSR_MC_STATUS_O)) - fatal = 1; /* context corrupt or overflow */ + ms_scope = cms_error_action(hdl, ismc, i, mcistatus, + gbl->gbl_addr, gbl->gbl_misc, gcl->gcl_ms_logout); - wrmsr(bank->bank_status, 0ULL); + if (pcc && ms_scope & CMS_ERRSCOPE_CURCONTEXT_OK) { + pcc = 0; + mcesp->mce_npcc_ok++; + gbl->gbl_disp |= CMI_ERRDISP_PCC_CLEARED; + } + + if (uc && ms_scope & CMS_ERRSCOPE_CLEARED_UC) { + uc = 0; + mcesp->mce_nuc_ok++; + gbl->gbl_disp |= CMI_ERRDISP_UC_CLEARED; + } + + if (uc) { + poisoned = (ms_scope & CMS_ERRSCOPE_POISONED) != 0; + if (poisoned) { + mcesp->mce_nuc_poisoned++; + gbl->gbl_disp |= CMI_ERRDISP_POISONED; + } + } + + if ((ms_scope & CMS_ERRSCOPE_IGNORE_ERR) == 0) { + /* + * We're not being instructed to ignore the error, + * so apply our standard disposition logic to it. + */ + if (uc && !poisoned) { + unconstrained++; + gbl->gbl_disp |= disp | + CMI_ERRDISP_UC_UNCONSTRAINED; + } + + if (pcc && ismc) { + curctxbad++; + gbl->gbl_disp |= disp | + CMI_ERRDISP_CURCTXBAD; + } + + /* + * Even if the above may not indicate that the error + * is terminal, model-specific support may insist + * that we treat it as such. Such errors wil be + * fatal even if discovered via poll. + */ + if (ms_scope & CMS_ERRSCOPE_FORCE_FATAL) { + forcefatal++; + mcesp->mce_forcefatal++; + gbl->gbl_disp |= disp | + CMI_ERRDISP_FORCEFATAL; + } + } else { + mcesp->mce_ignored++; + gbl->gbl_disp |= disp | CMI_ERRDISP_IGNORED; + } + } + + if (unconstrained > 0) + disp |= CMI_ERRDISP_UC_UNCONSTRAINED; + + if (curctxbad > 0) + disp |= CMI_ERRDISP_CURCTXBAD; + + if (forcefatal > 0) + disp |= CMI_ERRDISP_FORCEFATAL; + + if (gcpu_mca_queue != NULL) { + int how; + + if (ismc) { + how = cmi_mce_response(rp, disp) ? + ERRORQ_ASYNC : /* no panic, so arrange drain */ + ERRORQ_SYNC; /* panic flow will drain */ + } else { + how = (disp & CMI_ERRDISP_FORCEFATAL && + cmi_panic_on_ue()) ? + ERRORQ_SYNC : /* poller will panic */ + ERRORQ_ASYNC; /* no panic */ + } + + errorq_dispatch(gcpu_mca_queue, gcl, mca->gcpu_mca_lgsz, how); + } else if (disp != 0) { + gcpu_bleat(hdl, gcl); } - wrmsr(IA32_MSR_MCG_STATUS, 0); + mcesp->mce_disp = disp; + + return (disp); +} + +/* + * Gather error telemetry from our source, and then submit it for + * processing. + */ + +#define IS_MCE_CANDIDATE(status) (((status) & MSR_MC_STATUS_EN) != 0 && \ + ((status) & (MSR_MC_STATUS_UC | MSR_MC_STATUS_PCC)) != 0) + +#define STATUS_EQV(s1, s2) \ + (((s1) & ~MSR_MC_STATUS_OVER) == ((s2) & ~MSR_MC_STATUS_OVER)) + +static uint32_t gcpu_deferrred_polled_clears; + +void +gcpu_mca_logout(cmi_hdl_t hdl, struct regs *rp, uint64_t bankmask, + gcpu_mce_status_t *mcesp, boolean_t clrstatus) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + gcpu_mca_t *mca = &gcpu->gcpu_mca; + int nbanks = mca->gcpu_mca_nbanks; + gcpu_bank_logout_t *gbl, *pgbl; + gcpu_logout_t *gcl, *pgcl; + int ismc = (rp != NULL); + int ispoll = !ismc; + int i, nerr = 0; + cmi_errno_t err; + uint64_t mcg_status; + uint64_t disp; + uint64_t cap; + + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_STATUS, &mcg_status) != + CMI_SUCCESS || cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_CAP, &cap) != + CMI_SUCCESS) { + if (mcesp != NULL) + mcesp->mce_nerr = mcesp->mce_disp = 0; + return; + } + + if (ismc) { + gcl = mca->gcpu_mca_logout[GCPU_MCA_LOGOUT_EXCEPTION]; + } else { + int pidx = mca->gcpu_mca_nextpoll_idx; + int ppidx = (pidx == GCPU_MCA_LOGOUT_POLLER_1) ? + GCPU_MCA_LOGOUT_POLLER_2 : GCPU_MCA_LOGOUT_POLLER_1; + + gcl = mca->gcpu_mca_logout[pidx]; /* current logout */ + pgcl = mca->gcpu_mca_logout[ppidx]; /* previous logout */ + mca->gcpu_mca_nextpoll_idx = ppidx; /* switch next time */ + } + + gcl->gcl_timestamp = gethrtime_waitfree(); + gcl->gcl_mcg_status = mcg_status; + gcl->gcl_ip = rp ? rp->r_pc : 0; + + gcl->gcl_flags = (rp && USERMODE(rp->r_cs)) ? GCPU_GCL_F_PRIV : 0; + if (cap & MCG_CAP_TES_P) + gcl->gcl_flags |= GCPU_GCL_F_TES_P; + + for (i = 0, gbl = &gcl->gcl_data[0]; i < nbanks; i++, gbl++) { + uint64_t status, status2, addr, misc; + int retries = gcpu_mca_telemetry_retries; + + gbl->gbl_status = 0; + gbl->gbl_disp = 0; + gbl->gbl_clrdefcnt = 0; + + /* + * Only logout from MCA banks we have initialized from at + * least one core. If a core shares an MCA bank with another + * but perhaps lost the race to initialize it, then it must + * still be allowed to logout from the shared bank. + */ + if (!(gcpu->gcpu_shared->gcpus_actv_banks & 1 << i)) + continue; + + /* + * On a poll look only at the banks we've been asked to check. + */ + if (rp == NULL && !(bankmask & 1 << i)) + continue; + + + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, STATUS), &status) != + CMI_SUCCESS) + continue; +retry: + if (!(status & MSR_MC_STATUS_VAL)) + continue; + + addr = -1; + misc = 0; + + if (status & MSR_MC_STATUS_ADDRV) + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, ADDR), &addr); + + if (status & MSR_MC_STATUS_MISCV) + (void) cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, MISC), &misc); + + /* + * Allow the model-specific code to extract bank telemetry. + */ + cms_bank_logout(hdl, i, status, addr, misc, gcl->gcl_ms_logout); + + /* + * Not all cpu models assure us that the status/address/misc + * data will not change during the above sequence of MSR reads, + * or that it can only change by the addition of the OVerflow + * bit to the status register. If the status has changed + * other than in the overflow bit then we attempt to reread + * for a consistent snapshot, but eventually give up and + * go with what we've got. We only perform this check + * for a poll - a further #MC during a #MC will reset, and + * polled errors should not overwrite higher-priority + * trapping errors (but could set the overflow bit). + */ + if (ispoll && (err = cmi_hdl_rdmsr(hdl, IA32_MSR_MC(i, STATUS), + &status2)) == CMI_SUCCESS) { + if (!STATUS_EQV(status, status2)) { + if (retries-- > 0) { + status = status2; + goto retry; + } else { + gbl->gbl_disp |= + CMI_ERRDISP_INCONSISTENT; + } + } + } else if (ispoll && err != CMI_SUCCESS) { + gbl->gbl_disp |= CMI_ERRDISP_INCONSISTENT; + } + + nerr++; + gbl->gbl_status = status; + gbl->gbl_addr = addr; + gbl->gbl_misc = misc; + + if (clrstatus == B_FALSE) + goto serialize; + + /* + * For machine checks we always clear status here. For polls + * we must be a little more cautious since there is an + * outside chance that we may clear telemetry from a shared + * MCA bank on which a sibling core is machine checking. + * + * For polled observations of errors that look like they may + * produce a machine check (UC/PCC and ENabled, although these + * do not guarantee a machine check on error occurence) + * we will not clear the status at this wakeup unless + * we saw the same status at the previous poll. We will + * always process and log the current observations - it + * is only the clearing of MCi_STATUS which may be + * deferred until the next wakeup. + */ + if (ismc || !IS_MCE_CANDIDATE(status)) { + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MC(i, STATUS), 0ULL); + goto serialize; + } + + /* + * We have a polled observation of a machine check + * candidate. If we saw essentially the same status at the + * last poll then clear the status now since this appears + * not to be a #MC candidate after all. If we see quite + * different status now then do not clear, but reconsider at + * the next poll. In no actual machine check clears + * the status in the interim then the status should not + * keep changing forever (meaning we'd never clear it) + * since before long we'll simply have latched the highest- + * priority error and set the OVerflow bit. Nonetheless + * we count how many times we defer clearing and after + * a while insist on clearing the status. + */ + pgbl = &pgcl->gcl_data[i]; + if (pgbl->gbl_clrdefcnt != 0) { + /* We deferred clear on this bank at last wakeup */ + if (STATUS_EQV(status, pgcl->gcl_data[i].gbl_status) || + pgbl->gbl_clrdefcnt > 5) { + /* + * Status is unchanged so clear it now and, + * since we have already logged this info, + * avoid logging it again. + */ + gbl->gbl_status = 0; + nerr--; + (void) cmi_hdl_wrmsr(hdl, + IA32_MSR_MC(i, STATUS), 0ULL); + } else { + /* Record deferral for next wakeup */ + gbl->gbl_clrdefcnt = pgbl->gbl_clrdefcnt + 1; + } + } else { + /* Record initial deferral for next wakeup */ + gbl->gbl_clrdefcnt = 1; + gcpu_deferrred_polled_clears++; + } - log_enter(); +serialize: + /* + * Intel Vol 3A says to execute a serializing instruction + * here, ie CPUID. Well WRMSR is also defined to be + * serializing, so the status clear above should suffice. + * To be a good citizen, and since some clears are deferred, + * we'll execute a CPUID instruction here. + */ + { + struct cpuid_regs tmp; + (void) __cpuid_insn(&tmp); + } + } - 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"); + if (gcpu_mca_stack_flag) + gcl->gcl_stackdepth = getpcstack(gcl->gcl_stack, FM_STK_DEPTH); + else + gcl->gcl_stackdepth = 0; + + /* + * Decide our disposition for this error or errors, and submit for + * logging and subsequent diagnosis. + */ + if (nerr != 0) { + disp = gcpu_mca_process(hdl, rp, nerr, gcpu, gcl, ismc, mcesp); } else { - cmn_err(CE_WARN, "Machine-Check Exception in %s mode", - USERMODE(rp->r_cs) ? "user" : "kernel"); + disp = 0; + if (mcesp) { + mcesp->mce_nerr = mcesp->mce_disp = 0; + } } /* - * 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. + * Clear MCG_STATUS if MCIP is set (machine check in progress). + * If a second #MC had occured before now the system would have + * reset. We can only do thise once gcpu_mca_process has copied + * the logout structure. */ - 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 (ismc && mcg_status & MCG_STATUS_MCIP) + (void) cmi_hdl_wrmsr(hdl, IA32_MSR_MCG_STATUS, 0); - if (!(bstatus & MSR_MC_STATUS_VAL)) - continue; + /* + * At this point we have read and logged all telemetry that is visible + * under the MCA. On architectures for which the NorthBridge is + * on-chip this may include NB-observed errors, but where the NB + * is off chip it may have been the source of the #MC request and + * so we must call into the memory-controller driver to give it + * a chance to log errors. + */ + if (ismc) { + int willpanic = (cmi_mce_response(rp, disp) == 0); + cmi_mc_logout(hdl, 1, willpanic); + } +} - 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); +int gcpu_mca_trap_vomit_summary = 0; + +/* + * On a native machine check exception we come here from mcetrap via + * cmi_mca_trap. A machine check on one cpu of a chip does not trap others + * cpus of the chip, so it is possible that another cpu on this chip could + * initiate a poll while we're in the #mc handler; it is also possible that + * this trap has occured during a poll on this cpu. So we must acquire + * the chip-wide poll lock, but be careful to avoid deadlock. + * + * The 'data' pointer cannot be NULL due to init order. + */ +uint64_t +gcpu_mca_trap(cmi_hdl_t hdl, struct regs *rp) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + kmutex_t *poll_lock = NULL; + gcpu_mce_status_t mce; + uint64_t mcg_status; + int tooklock = 0; + + if (cmi_hdl_rdmsr(hdl, IA32_MSR_MCG_STATUS, &mcg_status) != + CMI_SUCCESS || !(mcg_status & MCG_STATUS_MCIP)) + return (0); + + /* + * Synchronize with any poller from another core that may happen + * to share access to one or more of the MCA banks. + */ + if (gcpu->gcpu_shared != NULL) + poll_lock = &gcpu->gcpu_shared->gcpus_poll_lock; + + if (poll_lock != NULL && !mutex_owned(poll_lock)) { + /* + * The lock is not owned by the thread we have + * interrupted. Spin for this adaptive lock. + */ + while (!mutex_tryenter(poll_lock)) { + while (mutex_owner(poll_lock) != NULL) + ; + } + tooklock = 1; + } + + gcpu_mca_logout(hdl, rp, 0, &mce, B_TRUE); + + if (tooklock) + mutex_exit(poll_lock); + + /* + * gcpu_mca_trap_vomit_summary may be set for debug assistance. + */ + if (mce.mce_nerr != 0 && gcpu_mca_trap_vomit_summary) { + cmn_err(CE_WARN, "MCE: %u errors, disp=0x%llx, " + "%u PCC (%u ok), " + "%u UC (%d ok, %u poisoned), " + "%u forcefatal, %u ignored", + mce.mce_nerr, (u_longlong_t)mce.mce_disp, + mce.mce_npcc, mce.mce_npcc_ok, + mce.mce_nuc, mce.mce_nuc_ok, mce.mce_nuc_poisoned, + mce.mce_forcefatal, mce.mce_ignored); + } + + return (mce.mce_disp); +} + +/*ARGSUSED*/ +void +gcpu_faulted_enter(cmi_hdl_t hdl) +{ + /* Nothing to do here */ +} + +/*ARGSUSED*/ +void +gcpu_faulted_exit(cmi_hdl_t hdl) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + + gcpu->gcpu_mca.gcpu_mca_flags |= GCPU_MCA_F_UNFAULTING; +} + +/* + * Write the requested values to the indicated MSRs. Having no knowledge + * of the model-specific requirements for writing to these model-specific + * registers, we will only blindly write to those MSRs if the 'force' + * argument is nonzero. That option should only be used in prototyping + * and debugging. + */ +/*ARGSUSED*/ +cmi_errno_t +gcpu_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs, + int force) +{ + int i, errs = 0; + + for (i = 0; i < nregs; i++) { + uint_t msr = regs[i].cmr_msrnum; + uint64_t val = regs[i].cmr_msrval; + + if (cms_present(hdl)) { + if (cms_msrinject(hdl, msr, val) != CMS_SUCCESS) + errs++; + } else if (force) { + errs += (cmi_hdl_wrmsr(hdl, msr, val) != CMI_SUCCESS); + } else { + errs++; } } - log_exit(); - return (fatal); + return (errs == 0 ? CMI_SUCCESS : CMIERR_UNKNOWN); } diff --git a/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_poll.c b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_poll.c new file mode 100644 index 0000000000..a0bdde9f05 --- /dev/null +++ b/usr/src/uts/i86pc/cpu/generic_cpu/gcpu_poll.c @@ -0,0 +1,352 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Generic x86 CPU MCA poller. + */ + +#include <sys/types.h> +#include <sys/sysmacros.h> +#include <sys/cyclic.h> +#include <sys/x86_archext.h> +#include <sys/mca_x86.h> +#include <sys/sdt.h> +#include <sys/cmn_err.h> + +#include "gcpu.h" + +uint_t gcpu_mca_poll_trace_nent = 100; +#ifdef DEBUG +int gcpu_mca_poll_trace_always = 1; +#else +int gcpu_mca_poll_trace_always = 0; +#endif + +cyclic_id_t gcpu_mca_poll_cycid; +hrtime_t gcpu_mca_poll_interval = NANOSEC * 10ULL; /* tuneable */ + +static kmutex_t mch_poll_lock; +static hrtime_t mch_poll_timestamp; +static cmi_hdl_t mch_poll_owner; + +/* + * Return nonzero of the given handle should poll the MCH. We stick with + * the same handle as before unless the timestamp has not been updated + * for a while. There is no need to keep a hold on the mch_poll_owner + * handle. + */ +static int +gcpu_mch_pollowner(cmi_hdl_t hdl) +{ + hrtime_t now = gethrtime_waitfree(); + int dopoll = 0; + + mutex_enter(&mch_poll_lock); + if (now - mch_poll_timestamp > 2 * gcpu_mca_poll_interval || + mch_poll_timestamp == 0) { + mch_poll_owner = hdl; + dopoll = 1; + } else if (mch_poll_owner == hdl) { + dopoll = 1; + } + + if (dopoll) + mch_poll_timestamp = now; + + mutex_exit(&mch_poll_lock); + return (dopoll); +} + +static void +gcpu_mca_poll_trace(gcpu_mca_poll_trace_ctl_t *ptc, uint8_t what, uint8_t nerr) +{ + uint_t next; + gcpu_mca_poll_trace_t *pt; + + DTRACE_PROBE2(gcpu__mca__poll__trace, uint32_t, what, uint32_t, nerr); + + if (ptc->mptc_tbufs == NULL) + return; /* poll trace buffer is disabled */ + + next = (ptc->mptc_curtrace + 1) % gcpu_mca_poll_trace_nent; + pt = &ptc->mptc_tbufs[next]; + + pt->mpt_when = 0; + pt->mpt_what = what; + + if (what == GCPU_MPT_WHAT_CYC_ERR) + pt->mpt_nerr = MIN(nerr, UINT8_MAX); + + pt->mpt_when = gethrtime_waitfree(); + ptc->mptc_curtrace = next; +} + +#ifndef __xpv +/* + * Perform a native poll of MCA state. + */ +static void +gcpu_ntv_mca_poll(cmi_hdl_t hdl, int what) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + gcpu_mca_t *mca = &gcpu->gcpu_mca; + gcpu_mca_poll_trace_ctl_t *ptc = &gcpu->gcpu_mca.gcpu_mca_polltrace; + gcpu_mce_status_t mce; + int willpanic; + + ASSERT(MUTEX_HELD(&gcpu->gcpu_shared->gcpus_poll_lock)); + + if (mca->gcpu_mca_flags & GCPU_MCA_F_UNFAULTING) { + int i; + + mca->gcpu_mca_flags &= ~GCPU_MCA_F_UNFAULTING; + gcpu_mca_poll_trace(ptc, GCPU_MPT_WHAT_UNFAULTING, 0); + + /* + * On the first cyclic poll after unfaulting a CPU we + * clear the status registers; see gcpu_faulted_exit + * for details. We don't do this if the poll was + * initiated manually (presumably from some injection + * activity). + */ + if (what == GCPU_MPT_WHAT_CYC_ERR) { + for (i = 0; i < mca->gcpu_mca_nbanks; i++) { + (void) cmi_hdl_wrmsr(hdl, + IA32_MSR_MC(i, STATUS), 0ULL); + } + return; + } + } + + /* + * Logout errors of the MCA banks of this cpu. + */ + gcpu_mca_logout(hdl, NULL, + cms_poll_ownermask(hdl, gcpu_mca_poll_interval), &mce, B_TRUE); + + gcpu_mca_poll_trace(ptc, what, mce.mce_nerr); + mca->gcpu_mca_lastpoll = gethrtime_waitfree(); + + willpanic = mce.mce_disp & CMI_ERRDISP_FORCEFATAL && cmi_panic_on_ue(); + + /* + * Call to the memory-controller driver which may report some + * errors not visible under the MCA (for off-chip NB). + * Since there is typically a single MCH we arrange that + * just one cpu perform this task at each cyclic fire. + */ + if (gcpu_mch_pollowner(hdl)) + cmi_mc_logout(hdl, 0, willpanic); + + /* + * In the common case any polled error is considered non-fatal, + * even if it indicates PCC or UC etc. The only condition on which + * we will panic for a polled error is if model-specific support + * forces the error to be terminal regardless of how it is + * encountered. + */ + if (willpanic) { +#ifdef DEBUG + cmn_err(CE_WARN, "MCA Poll: %u errors, disp=0x%llx, " + "%u PCC (%u ok), " + "%u UC (%u ok, %u poisoned), " + "%u forcefatal, %u ignored", + mce.mce_nerr, (u_longlong_t)mce.mce_disp, + mce.mce_npcc, mce.mce_npcc_ok, + mce.mce_nuc, mce.mce_nuc_ok, mce.mce_nuc_poisoned, + mce.mce_forcefatal, mce.mce_ignored); + +#endif + fm_panic("Unrecoverable Machine-Check Exception (Polled)"); + } +} + +/* + * See gcpu_mca_trap for an explanation of why preemption is disabled here. + * Note that we disable preemption and then contend for an adaptive mutex - + * we could block during the mutex operation, but once we return with the + * mutex held we nust perform no operation that can block and we cannot + * be preempted so we will stay on cpu for the duration. The disabling + * of preemption also means we cannot migrate cpus once we have returned + * with the mutex held - cyclic invocations can't migrate, anyway, but + * others could if they have failed to bind before this point. + */ +static void +gcpu_ntv_mca_poll_wrapper(cmi_hdl_t hdl, int what) +{ + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + + kpreempt_disable(); + mutex_enter(&gcpu->gcpu_shared->gcpus_poll_lock); + gcpu_ntv_mca_poll(hdl, what); + mutex_exit(&gcpu->gcpu_shared->gcpus_poll_lock); + kpreempt_enable(); +} + +static void +gcpu_ntv_mca_poll_cyclic(void *arg) +{ + gcpu_ntv_mca_poll_wrapper((cmi_hdl_t)arg, GCPU_MPT_WHAT_CYC_ERR); +} + + +/*ARGSUSED*/ +static void +gcpu_ntv_mca_poll_online(void *arg, cpu_t *cp, cyc_handler_t *cyh, + cyc_time_t *cyt) +{ + cmi_hdl_t hdl; + + /* cmi_hdl_lookup holds any handle it finds - release in offline */ + if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(cp), + cmi_ntv_hwcoreid(cp), cmi_ntv_hwstrandid(cp))) == NULL) + return; + + cyt->cyt_when = 0; + cyt->cyt_interval = gcpu_mca_poll_interval; + cyh->cyh_func = gcpu_ntv_mca_poll_cyclic; + cyh->cyh_arg = (void *)hdl; + cyh->cyh_level = CY_LOW_LEVEL; +} + +/*ARGSUSED*/ +static void +gcpu_ntv_mca_poll_offline(void *arg, cpu_t *cpu, void *cyh_arg) +{ + cmi_hdl_t hdl = (cmi_hdl_t)cyh_arg; + + cmi_hdl_rele(hdl); +} +#endif /* __xpv */ + +/* + * gcpu_mca_poll_init is called from gcpu_mca_init for each cpu handle + * that we initialize for. It should prepare for polling by allocating + * control structures and the like, but must not kick polling off yet. + * + * In the native case our polling technique (see gcpu_mca_poll_start) will + * be to install an omnipresent cyclic to fire on all online cpus (cpu_t), + * and they will poll the real hardware beneath them. + * + * In the xVM MCA case the hypervisor performs polling and makes telemetry + * available to dom0 - a cyclic on each virtual cpu is inappropriate. + * Instead we will create a single unbound cyclic which will consume the + * hypervisor-provided telemetry when it fires, and submit it into + * common logging code. + */ + +void +gcpu_mca_poll_init(cmi_hdl_t hdl) +{ + gcpu_mca_poll_trace_t *tbufs = NULL; + + switch (cmi_hdl_class(hdl)) { + case CMI_HDL_NATIVE: { + gcpu_data_t *gcpu = cmi_hdl_getcmidata(hdl); + gcpu_mca_t *mca = &gcpu->gcpu_mca; + + if (gcpu_mca_poll_trace_always) { + tbufs = kmem_zalloc(sizeof (gcpu_mca_poll_trace_t) * + gcpu_mca_poll_trace_nent, KM_SLEEP); + } + mca->gcpu_mca_polltrace.mptc_tbufs = tbufs; + mca->gcpu_mca_polltrace.mptc_curtrace = 0; + break; + } + + case CMI_HDL_SOLARIS_xVM_MCA: + /* + * Implementation should move the kmem_alloc above to before + * the switch, and stash the trace buffer and current record + * pointer in a static structure. This should be done + * just once, despite this init function potentially being + * called multiple times. + */ + /*FALLTHRU*/ + + default: + break; + } +} + +static void +gcpu_ntv_mca_poll_start(void) +{ +#ifndef __xpv + cyc_omni_handler_t cyo; + + if (gcpu_mca_poll_interval == 0) + return; + + cyo.cyo_online = gcpu_ntv_mca_poll_online; + cyo.cyo_offline = gcpu_ntv_mca_poll_offline; + cyo.cyo_arg = NULL; + + mutex_enter(&cpu_lock); + gcpu_mca_poll_cycid = cyclic_add_omni(&cyo); + mutex_exit(&cpu_lock); +#endif /* __xpv */ +} + +void +gcpu_mca_poll_start(cmi_hdl_t hdl) +{ + switch (cmi_hdl_class(hdl)) { + case CMI_HDL_NATIVE: + gcpu_ntv_mca_poll_start(); + break; + + case CMI_HDL_SOLARIS_xVM_MCA: + /* + * Implementation should call a new function to install + * an unbound cyclic that will process hypervisor-provided + * telemetry. + */ + /*FALLTHRU*/ + + default: + break; + } +} + +void +gcpu_hdl_poke(cmi_hdl_t hdl) +{ + switch (cmi_hdl_class(hdl)) { + case CMI_HDL_NATIVE: + gcpu_ntv_mca_poll_wrapper(hdl, GCPU_MPT_WHAT_POKE_ERR); + break; + + case CMI_HDL_SOLARIS_xVM_MCA: + /* + * Implementation will call the xPV poll wrapper. + */ + default: + break; + } +} diff --git a/usr/src/uts/i86pc/cpu/genuineintel/gintel_main.c b/usr/src/uts/i86pc/cpu/genuineintel/gintel_main.c new file mode 100644 index 0000000000..40ed19c68c --- /dev/null +++ b/usr/src/uts/i86pc/cpu/genuineintel/gintel_main.c @@ -0,0 +1,143 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Intel model-specific support. Right now all this conists of is + * to modify the ereport subclass to produce different ereport classes + * so that we can have different diagnosis rules and corresponding faults. + */ + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/modctl.h> +#include <sys/mca_x86.h> +#include <sys/cpu_module_ms_impl.h> +#include <sys/mc_intel.h> +#include <sys/pci_cfgspace.h> + +int gintel_ms_support_disable = 0; +int gintel_error_action_return = 0; +int gintel_ms_unconstrained = 0; + +/*ARGSUSED*/ +int +gintel_init(cmi_hdl_t hdl, void **datap) +{ + uint32_t nb_chipset; + + if (gintel_ms_support_disable) + return (ENOTSUP); + + if (!(x86_feature & X86_MCA)) + return (ENOTSUP); + + nb_chipset = (*pci_getl_func)(0, 0, 0, 0x0); + switch (nb_chipset) { + case INTEL_NB_7300: + case INTEL_NB_5000P: + case INTEL_NB_5000X: + case INTEL_NB_5000V: + case INTEL_NB_5000Z: + if (!gintel_ms_unconstrained) + gintel_error_action_return |= CMS_ERRSCOPE_POISONED; + break; + default: + break; + } + return (0); +} + +/*ARGSUSED*/ +uint32_t +gintel_error_action(cmi_hdl_t hdl, int ismc, int bank, + uint64_t status, uint64_t addr, uint64_t misc, void *mslogout) +{ + return (gintel_error_action_return); +} + +/*ARGSUSED*/ +void +gintel_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, + const char **cpuclsp, const char **leafclsp) +{ + *cpuclsp = FM_EREPORT_CPU_INTEL; +} + +cms_api_ver_t _cms_api_version = CMS_API_VERSION_0; + +const cms_ops_t _cms_ops = { + gintel_init, /* cms_init */ + NULL, /* cms_post_startup */ + NULL, /* cms_post_mpstartup */ + NULL, /* cms_logout_size */ + NULL, /* cms_mcgctl_val */ + NULL, /* cms_bankctl_skipinit */ + NULL, /* cms_bankctl_val */ + NULL, /* cms_bankstatus_skipinit */ + NULL, /* cms_bankstatus_val */ + NULL, /* cms_mca_init */ + NULL, /* cms_poll_ownermask */ + NULL, /* cms_bank_logout */ + gintel_error_action, /* cms_error_action */ + NULL, /* cms_disp_match */ + gintel_ereport_class, /* cms_ereport_class */ + NULL, /* cms_ereport_detector */ + NULL, /* cms_ereport_includestack */ + NULL, /* cms_ereport_add_logout */ + NULL, /* cms_msrinject */ + NULL, /* cms_fini */ +}; + +static struct modlcpu modlcpu = { + &mod_cpuops, + "Generic Intel model-specific MCA" +}; + +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/scripts/ao_gendisp.pl b/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl index fa73ce0fa4..6c64e672a3 100644 --- a/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl +++ b/usr/src/uts/i86pc/cpu/scripts/ao_gendisp.pl @@ -21,7 +21,7 @@ # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -111,6 +111,7 @@ sub print_errout() { } sub print_header() { + print "#include <sys/mca_x86.h>\n"; print "#include \"ao_mca_disp.h\"\n\n"; } @@ -244,30 +245,30 @@ sub state_code() { 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', + 'err' => 'err', + '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', + 'src' => 'src', + 'res' => 'res', + 'obs' => 'obs', + 'gen' => 'gen', '-' => '-' ); my %t_values = ( 0 => 1, 1 => 1, '-' => 1 ); my %ii_values = ( - mem => 'mem', - io => 'io', - gen => 'gen', + 'mem' => 'mem', + 'io' => 'io', + 'gen' => 'gen', '-' => '-' ); my $instance = &code_lines(); @@ -308,10 +309,10 @@ sub state_code() { $::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKBUS(" . "0, " . # pp - "AMD_ERRCODE_T_" . ($t ? "TIMEOUT" : "NONE") . ", " . + "MCAX86_ERRCODE_T_" . ($t ? "TIMEOUT" : "NONE") . ", " . "0, " . # r4 "0, " . # ii - "AMD_ERRCODE_LL_$ll),\n"); + "MCAX86_ERRCODE_LL_$ll),\n"); } elsif ($type eq "mem") { if ($r4 eq "-" || $tt eq "-" || $ll eq "-" || @@ -321,8 +322,8 @@ sub state_code() { $::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKMEM(" . "0, " . # r4 - "AMD_ERRCODE_TT_$tt, " . - "AMD_ERRCODE_LL_$ll),\n"); + "MCAX86_ERRCODE_TT_$tt, " . + "MCAX86_ERRCODE_LL_$ll),\n"); } elsif ($type eq "tlb") { if ($tt eq "-" || $ll eq "-" || @@ -331,8 +332,8 @@ sub state_code() { } $::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKTLB(" . - "AMD_ERRCODE_TT_$tt, " . - "AMD_ERRCODE_LL_$ll),\n"); + "MCAX86_ERRCODE_TT_$tt, " . + "MCAX86_ERRCODE_LL_$ll),\n"); } else { &parsebail("unknown code type `$type'"); } @@ -380,6 +381,14 @@ sub state_flags() { &errout(&print_bits("flags", @flags)); } +sub state_errtype() { + my @types = split(/,\s*/, $_[0]); + + @types = map { tr/[a-z]/[A-Z]/; "AO_AED_ET_" . $_; } @types; + + &errout(&print_bits("errtype", @types)); +} + my %stateparse = ( funcunit => [ \&state_funcunit, 'desc' ], desc => [ \&state_desc, 'error' ], @@ -388,7 +397,8 @@ my %stateparse = ( 'mask off' => [ \&state_mask_off, 'code' ], code => [ \&state_code, 'code|panic' ], panic => [ \&state_panic, 'flags' ], - flags => [ \&state_flags, 'initial' ] + flags => [ \&state_flags, 'errtype' ], + errtype => [ \&state_errtype, 'initial' ] ); usage unless (@ARGV == 1); diff --git a/usr/src/uts/i86pc/generic_cpu/Makefile b/usr/src/uts/i86pc/generic_cpu/Makefile index f23d9dd369..4c4d8bc280 100644 --- a/usr/src/uts/i86pc/generic_cpu/Makefile +++ b/usr/src/uts/i86pc/generic_cpu/Makefile @@ -1,9 +1,8 @@ # 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. +# Common Development and Distribution License (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. @@ -18,7 +17,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" diff --git a/usr/src/uts/i86pc/genuineintel/Makefile b/usr/src/uts/i86pc/genuineintel/Makefile new file mode 100644 index 0000000000..717eff899f --- /dev/null +++ b/usr/src/uts/i86pc/genuineintel/Makefile @@ -0,0 +1,90 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 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_ms.GenuineIntel +# +OBJECTS = $(CPU_GENINTEL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(CPU_GENINTEL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_CPU_DIR)/$(MODULE) + +SRCDIR = ../cpu/genuineintel + +# +# Include common rules. +# +include ../cpu/Makefile.cpu + +# +# Our lint library has a different name from that of the module we build. +# +LINT_MODULE = genuineintel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(LINT_MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides and additions +# +CPPFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) +ASFLAGS += -I$(SRCDIR) -I$(OBJS_DIR) +LDFLAGS += -dy -N misc/acpica + +# +# 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/intel_nb5000/Makefile b/usr/src/uts/i86pc/intel_nb5000/Makefile new file mode 100644 index 0000000000..80fe41789c --- /dev/null +++ b/usr/src/uts/i86pc/intel_nb5000/Makefile @@ -0,0 +1,81 @@ +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 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 = intel_nb5000 +# +INTEL_NB5000_OBJS = intel_nb5000.o intel_nbdrv.o dimm_addr.o nb_pci_cfg.o \ + nb5000_init.o +OBJECTS = $(INTEL_NB5000_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(INTEL_NB5000_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/i86pc/io/intel_nb5000 + +# +# Include common rules. +# +include $(UTSBASE)/i86pc/Makefile.i86pc + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(LINT_MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +CPPFLAGS += -I$(SRC)/uts/i86pc/cpu/genuineintel + +# +# 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/intel_nb5000/dimm_addr.c b/usr/src/uts/i86pc/io/intel_nb5000/dimm_addr.c new file mode 100644 index 0000000000..d5d91f620b --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/dimm_addr.c @@ -0,0 +1,336 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/kmem.h> +#include <sys/mc.h> +#include <sys/nvpair.h> +#include <sys/cpu_module_impl.h> +#include <sys/fm/protocol.h> +#include <sys/cmn_err.h> +#include <sys/sunddi.h> +#include "dimm_addr.h" +#include "nb_log.h" +#include "rank.h" +#include "dimm_phys.h" +#include "nb5000.h" + +struct dimm_geometry **dimm_geometry; +struct rank_base *rank_base; + +uint64_t +dimm_getphys(int branch, int rank, int bank, int ras, int cas) +{ + uint8_t i; + uint64_t m; + uint64_t pa; + struct rank_base *rp; + struct rank_geometry *rgp; + + rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank]; + rgp = (struct rank_geometry *)rp->rank_geometry; + if (rgp == NULL) + return (-1LL); + pa = rp->base; + + for (i = 0, m = 1; bank; i++, m <<= 1) { + if ((bank & m) != 0 && rgp->bank[i] != 0xff) { + pa += 1 << rgp->bank[i]; + bank &= ~m; + } + } + for (i = 0, m = 1; cas; i++, m <<= 1) { + if ((cas & m) != 0 && rgp->col[i] != 0xff) { + pa += 1 << rgp->col[i]; + cas &= ~m; + } + } + for (i = 0, m = 1; ras; i++, m <<= 1) { + if ((ras & m) != 0 && rgp->row[i] != 0xff) { + pa += 1 << rgp->row[i]; + ras &= ~m; + } + } + if (rp->interleave > 1) { + i = 0; + if (rp->branch_interleave) { + if (branch) { + pa += 1 << rgp->interleave[i]; + } + i++; + } + if ((rp->way & 1) != 0) + pa += 1 << rgp->interleave[i]; + i++; + if ((rp->way & 2) != 0) + pa += 1 << rgp->interleave[i]; + } + if (rp->hole && pa >= rp->hole) + pa += rp->hole_size; + return (pa); +} + +uint64_t +dimm_getoffset(int branch, int rank, int bank, int ras, int cas) +{ + uint8_t i; + uint64_t m; + uint64_t offset; + struct dimm_geometry *dgp; + struct rank_geometry *rgp; + struct rank_base *rp; + uint64_t pa; + uint64_t cal_pa; + + rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank]; + dgp = dimm_geometry[(branch * nb_dimms_per_channel) + rank/2]; + if (dgp == NULL) + return (TCODE_OFFSET(rank, bank, ras, cas)); + rgp = (struct rank_geometry *)&dgp->rank_geometry[0]; + offset = 0; + pa = dimm_getphys(branch, rank, bank, ras, cas) & PAGEMASK; + + for (i = 0, m = 1; bank; i++, m <<= 1) { + if ((bank & m) != 0 && rgp->bank[i] != 0xff) { + offset += 1 << rgp->bank[i]; + bank &= ~m; + } + } + for (i = 0, m = 1; cas; i++, m <<= 1) { + if ((cas & m) != 0 && rgp->col[i] != 0xff) { + offset += 1 << rgp->col[i]; + cas &= ~m; + } + } + for (i = 0, m = 1; ras; i++, m <<= 1) { + if ((ras & m) != 0 && rgp->row[i] != 0xff) { + offset += 1 << rgp->row[i]; + ras &= ~m; + } + } + cal_pa = rp->base + (offset * rp->interleave); + if (rp->hole && cal_pa >= rp->hole) + cal_pa += rp->hole_size; + cal_pa &= PAGEMASK; + + if (pa != cal_pa) { + return (-1LL); + } + return (offset & PAGEMASK); +} + +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] = MC_INVALNUM; + + 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, "memory-controller") == 0) + unump->unum_mc = (int)v; + else if (strcmp(hcnm, "dram-channel") == 0) + unump->unum_cs = (int)v; + else if (strcmp(hcnm, "dimm") == 0) + unump->unum_dimms[0] = (int)v; + else if (strcmp(hcnm, "rank") == 0) + unump->unum_rank = (int)v; + } + + unump->unum_offset = offset; + + return (1); +} + +/*ARGSUSED*/ +static cmi_errno_t +inb_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, + uint32_t synd, int syndtype, mc_unum_t *unump) +{ + struct rank_base *rp; + int i; + int last; + uint64_t offset; + cmi_errno_t rt = CMIERR_UNKNOWN; + + last = nb_dimms_per_channel * nb_number_memory_controllers; + for (i = 0; i < last; i++) { + rp = &rank_base[i]; + if (rp && pa >= rp->base && pa < rp->limit) + break; + } + if (i < last) { + offset = pa - rp->base; + if (offset > rp->hole) + offset -= rp->hole_size; + unump->unum_offset = offset / rp->interleave; + unump->unum_mc = i / nb_dimms_per_channel; + unump->unum_cs = 0; + unump->unum_rank = i % nb_dimms_per_channel; + rt = CMI_SUCCESS; + } + return (rt); +} + +/*ARGSUSED*/ +static cmi_errno_t +inb_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap) +{ + mc_unum_t unum; + uint64_t pa; + struct rank_base *rp; + + if (unump == NULL) { + if (!fmri2unum(nvl, &unum)) + return (CMI_SUCCESS); + unump = &unum; + } + if (unump->unum_offset & OFFSET_ROW_BANK_COL) { + pa = dimm_getphys(unump->unum_mc, + TCODE_OFFSET_RANK(unump->unum_offset), + TCODE_OFFSET_BANK(unump->unum_offset), + TCODE_OFFSET_RAS(unump->unum_offset), + TCODE_OFFSET_CAS(unump->unum_offset)); + if (pa == -1LL) + return (CMIERR_MC_NOADDR); + *pap = pa; + return (CMI_SUCCESS); + } + rp = &rank_base[(unump->unum_mc * nb_dimms_per_channel * 2) + + unump->unum_rank]; + pa = rp->base + (unump->unum_offset * rp->interleave); + + if (rp->hole && pa >= rp->hole) + pa += rp->hole_size; + *pap = pa; + return (CMI_SUCCESS); +} + +void +dimm_init() +{ + dimm_geometry = kmem_zalloc(sizeof (void *) * + nb_number_memory_controllers * nb_dimms_per_channel, KM_SLEEP); + rank_base = kmem_zalloc(sizeof (struct rank_base) * + nb_number_memory_controllers * nb_dimms_per_channel * 2, KM_SLEEP); +} + +void +dimm_fini() +{ + kmem_free(dimm_geometry, sizeof (void *) * + nb_number_memory_controllers * nb_dimms_per_channel); + dimm_geometry = 0; + kmem_free(rank_base, sizeof (struct rank_base) * + nb_number_memory_controllers * nb_dimms_per_channel * 2); + rank_base = 0; +} + +void +dimm_add_geometry(int branch, int dimm, int nbanks, int width, int ncolumn, + int nrow) +{ + int i; + for (i = 0; i < dimm_types; i++) { + if (dimm_data[i].row_nbits == nrow && + dimm_data[i].col_nbits == ncolumn && + dimm_data[i].width == width && + (1 << dimm_data[i].bank_nbits) == nbanks) { + dimm_geometry[(branch * nb_dimms_per_channel) + dimm] = + &dimm_data[i]; + break; + } + } +} + +void +dimm_add_rank(int branch, int rank, int branch_interleave, int way, + uint64_t base, uint32_t hole, uint32_t hole_size, int interleave, + uint64_t limit) +{ + struct dimm_geometry *dimm; + struct rank_base *rp; + int interleave_nbits; + + dimm = dimm_geometry[(branch * nb_dimms_per_channel) + (rank / 2)]; + rp = &rank_base[(branch * nb_dimms_per_channel * 2) + rank]; + if (interleave == 1) + interleave_nbits = 0; + else if (interleave == 2) + interleave_nbits = 1; + else if (interleave == 4) + interleave_nbits = 2; + else + interleave_nbits = 3; + rp->branch_interleave = branch_interleave; + rp->way = way; + rp->base = base; + rp->hole = hole; + rp->hole_size = hole_size; + rp->interleave = interleave; + rp->limit = limit; + if (dimm) + rp->rank_geometry = &dimm->rank_geometry[interleave_nbits]; + else + rp->rank_geometry = 0; +} + +static const cmi_mc_ops_t inb_mc_ops = { + inb_patounum, + inb_unumtopa, + nb_error_trap /* cmi_mc_logout */ +}; + +/*ARGSUSED*/ +int +inb_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3) +{ + cmi_mc_register(hdl, &inb_mc_ops, NULL); + return (CMI_HDL_WALK_NEXT); +} diff --git a/usr/src/uts/i86pc/io/intel_nb5000/dimm_addr.h b/usr/src/uts/i86pc/io/intel_nb5000/dimm_addr.h new file mode 100644 index 0000000000..b21fd09160 --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/dimm_addr.h @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DIMM_ADDR_H +#define _DIMM_ADDR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Map for memory address translation for each interleave */ + +struct rank_geometry { + uint8_t row[16]; /* Address bit associated with row bit */ + uint8_t bank[3]; /* Address bit associated with bank bit */ + uint8_t col[13]; /* Address bit associated with column bit */ + uint8_t interleave[3]; /* Address bit associated with interleave bit */ +}; + +struct dimm_geometry { + uint8_t row_nbits; /* number of row bits */ + uint8_t col_nbits; /* number of column bits */ + uint8_t bank_nbits; /* number of bank bits */ + uint8_t width; /* width */ + struct rank_geometry rank_geometry[4]; /* for interleave 1,2,4,8 */ +} dimm_data[1]; + +int dimm_types = sizeof (dimm_data) / sizeof (struct dimm_geometry); + +#ifdef __cplusplus +} +#endif + +#endif /* _DIMM_ADDR_H */ diff --git a/usr/src/uts/i86pc/io/intel_nb5000/dimm_phys.h b/usr/src/uts/i86pc/io/intel_nb5000/dimm_phys.h new file mode 100644 index 0000000000..f035ddb64a --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/dimm_phys.h @@ -0,0 +1,63 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DIMM_PHYS_H +#define _DIMM_PHYS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define OFFSET_ROW_BANK_COL 0x8000000000000000ULL +#define OFFSET_RANK_SHIFT 52 +#define OFFSET_RAS_SHIFT 32 +#define OFFSET_BANK_SHIFT 24 +#define TCODE_OFFSET(rank, bank, ras, cas) (OFFSET_ROW_BANK_COL | \ + ((uint64_t)(rank) << OFFSET_RANK_SHIFT) | \ + ((uint64_t)(ras) << OFFSET_RAS_SHIFT) | \ + ((uint64_t)(bank) << OFFSET_BANK_SHIFT) | (cas)) + +#define TCODE_OFFSET_RANK(tcode) (((tcode) >> OFFSET_RANK_SHIFT) & RANK_MASK) +#define TCODE_OFFSET_RAS(tcode) (((tcode) >> OFFSET_RAS_SHIFT) & RAS_MASK) +#define TCODE_OFFSET_BANK(tcode) (((tcode) >> OFFSET_BANK_SHIFT) & BANK_MASK) +#define TCODE_OFFSET_CAS(tcode) ((tcode) & CAS_MASK) + +extern void dimm_init(void); +extern void dimm_fini(void); +extern void dimm_add_rank(int, int, int, int, uint64_t, uint32_t, uint32_t, + int, uint64_t); +extern void dimm_add_geometry(int, int, int, int, int, int); + +extern uint64_t dimm_getoffset(int, int, int, int, int); +extern uint64_t dimm_getphys(int, int, int, int, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _DIMM_PHYS_H */ diff --git a/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.c b/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.c new file mode 100644 index 0000000000..2b0a03f2f8 --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.c @@ -0,0 +1,1325 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/errno.h> +#include <sys/log.h> +#include <sys/systm.h> +#include <sys/modctl.h> +#include <sys/errorq.h> +#include <sys/controlregs.h> +#include <sys/fm/util.h> +#include <sys/fm/protocol.h> +#include <sys/sysevent.h> +#include <sys/pghw.h> +#include <sys/cyclic.h> +#include <sys/pci_cfgspace.h> +#include <sys/mc_intel.h> +#include <sys/cpu_module_impl.h> +#include <sys/smbios.h> +#include "nb5000.h" +#include "nb_log.h" +#include "dimm_phys.h" + +static uint32_t uerrcnt[2]; +static uint32_t cerrcnt[2]; +static nb_logout_t nb_log; + +struct mch_error_code { + int intel_error_list; /* error number in Chipset Error List */ + uint32_t emask; /* mask for machine check */ + uint32_t error_bit; /* error bit in fault register */ +}; + +static struct mch_error_code fat_fbd_error_code[] = { + { 23, EMASK_FBD_M23, ERR_FAT_FBD_M23 }, + { 3, EMASK_FBD_M3, ERR_FAT_FBD_M3 }, + { 2, EMASK_FBD_M2, ERR_FAT_FBD_M2 }, + { 1, EMASK_FBD_M1, ERR_FAT_FBD_M1 } +}; + +static int +intel_fat_fbd_err(uint32_t fat_fbd) +{ + int rt = -1; + int nerr = 0; + uint32_t emask_fbd = 0; + int i; + int sz; + + sz = sizeof (fat_fbd_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (fat_fbd & fat_fbd_error_code[i].error_bit) { + rt = fat_fbd_error_code[i].intel_error_list; + emask_fbd |= fat_fbd_error_code[i].emask; + nerr++; + } + } + + if (emask_fbd) + nb_fbd_mask_mc(emask_fbd); + if (nerr > 1) + rt = -1; + return (rt); +} + +static char * +fat_memory_error(const nb_regs_t *rp, void *data) +{ + int channel; + uint32_t ferr_fat_fbd, nrecmemb; + uint16_t nrecmema; + char *intr = "nb.unknown"; + nb_mem_scatchpad_t *sp = &((nb_scatchpad_t *)data)->ms; + + ferr_fat_fbd = rp->nb.fat_fbd_regs.ferr_fat_fbd; + sp->intel_error_list = intel_fat_fbd_err(ferr_fat_fbd); + channel = (ferr_fat_fbd >> 28) & 3; + sp->branch = channel >> 1; + sp->channel = channel; + if ((ferr_fat_fbd & (ERR_FAT_FBD_M2|ERR_FAT_FBD_M1)) != 0) { + if ((ferr_fat_fbd & ERR_FAT_FBD_M1) != 0) + intr = "nb.fbd.alert"; /* Alert on FB-DIMM M1 */ + else + intr = "nb.fbd.crc"; /* CRC error FB_DIMM M2 */ + nrecmema = rp->nb.fat_fbd_regs.nrecmema; + nrecmemb = rp->nb.fat_fbd_regs.nrecmemb; + sp->rank = (nrecmema >> 8) & RANK_MASK; + sp->dimm = sp->rank >> 1; + sp->bank = (nrecmema >> 12) & BANK_MASK; + sp->cas = (nrecmemb >> 16) & CAS_MASK; + sp->ras = nrecmemb & RAS_MASK; + sp->pa = dimm_getphys(sp->branch, sp->rank, sp->bank, sp->ras, + sp->cas); + sp->offset = dimm_getoffset(sp->branch, sp->rank, sp->bank, + sp->ras, sp->cas); + } else { + if ((ferr_fat_fbd & ERR_FAT_FBD_M3) != 0) + intr = "nb.fbd.otf"; /* thermal temp > Tmid M3 */ + else if ((ferr_fat_fbd & ERR_FAT_FBD_M23) != 0) { + intr = "nb.fbd.reset_timeout"; + sp->channel = -1; + } + sp->rank = -1; + sp->dimm = -1; + sp->bank = -1; + sp->cas = -1; + sp->ras = -1; + sp->pa = -1LL; + sp->offset = -1; + } + return (intr); +} + + +static struct mch_error_code nf_fbd_error_code[] = { + { 28, EMASK_FBD_M28, ERR_NF_FBD_M28 }, + { 27, EMASK_FBD_M27, ERR_NF_FBD_M27 }, + { 26, EMASK_FBD_M26, ERR_NF_FBD_M26 }, + { 25, EMASK_FBD_M25, ERR_NF_FBD_M25 }, + { 22, EMASK_FBD_M22, ERR_NF_FBD_M22 }, + { 21, EMASK_FBD_M21, ERR_NF_FBD_M21 }, + { 20, EMASK_FBD_M20, ERR_NF_FBD_M20 }, + { 19, EMASK_FBD_M19, ERR_NF_FBD_M19 }, + { 18, EMASK_FBD_M18, ERR_NF_FBD_M18 }, + { 17, EMASK_FBD_M17, ERR_NF_FBD_M17 }, + { 15, EMASK_FBD_M15, ERR_NF_FBD_M15 }, + { 14, EMASK_FBD_M14, ERR_NF_FBD_M14 }, + { 13, EMASK_FBD_M13, ERR_NF_FBD_M13 }, + { 12, EMASK_FBD_M12, ERR_NF_FBD_M12 }, + { 11, EMASK_FBD_M11, ERR_NF_FBD_M11 }, + { 10, EMASK_FBD_M10, ERR_NF_FBD_M10 }, + { 9, EMASK_FBD_M9, ERR_NF_FBD_M9 }, + { 8, EMASK_FBD_M8, ERR_NF_FBD_M8 }, + { 7, EMASK_FBD_M7, ERR_NF_FBD_M7 }, + { 6, EMASK_FBD_M6, ERR_NF_FBD_M6 }, + { 5, EMASK_FBD_M5, ERR_NF_FBD_M5 }, + { 4, EMASK_FBD_M4, ERR_NF_FBD_M4 } +}; + +static int +intel_nf_fbd_err(uint32_t nf_fbd) +{ + int rt = -1; + int nerr = 0; + uint32_t emask_fbd = 0; + int i; + int sz; + + sz = sizeof (nf_fbd_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (nf_fbd & nf_fbd_error_code[i].error_bit) { + rt = nf_fbd_error_code[i].intel_error_list; + emask_fbd |= nf_fbd_error_code[i].emask; + nerr++; + } + } + if (emask_fbd) + nb_fbd_mask_mc(emask_fbd); + if (nerr > 1) + rt = -1; + return (rt); +} + +static char * +nf_memory_error(const nb_regs_t *rp, void *data) +{ + uint32_t ferr_nf_fbd, recmemb, redmemb; + uint16_t recmema; + int branch, channel, ecc_locator; + char *intr = "nb.unknown"; + nb_mem_scatchpad_t *sp = &((nb_scatchpad_t *)data)->ms; + + ferr_nf_fbd = rp->nb.nf_fbd_regs.ferr_nf_fbd; + sp->intel_error_list = intel_nf_fbd_err(ferr_nf_fbd); + channel = (ferr_nf_fbd >> ERR_FBD_CH_SHIFT) & 3; + branch = channel >> 1; + sp->branch = branch; + sp->channel = channel; + sp->rank = -1; + sp->dimm = -1; + sp->bank = -1; + sp->cas = -1; + sp->ras = -1LL; + sp->pa = -1LL; + sp->offset = -1; + if (ferr_nf_fbd & ERR_NF_FBD_MASK) { + if (ferr_nf_fbd & ERR_NF_FBD_ECC_UE) { + /* + * uncorrectable ECC M4 - M12 + * we can only isolate to pair of dimms + * for single dimm configuration let eversholt + * sort it out with out needing a special rule + */ + sp->channel = -1; + recmema = rp->nb.nf_fbd_regs.recmema; + recmemb = rp->nb.nf_fbd_regs.recmemb; + sp->rank = (recmema >> 8) & RANK_MASK; + sp->bank = (recmema >> 12) & BANK_MASK; + sp->cas = (recmemb >> 16) & CAS_MASK; + sp->ras = recmemb & RAS_MASK; + intr = "nb.mem_ue"; + } else if (ferr_nf_fbd & ERR_NF_FBD_M13) { + /* + * write error M13 + * we can only isolate to pair of dimms + */ + sp->channel = -1; + if (nb_mode != NB_MEMORY_MIRROR) { + recmema = rp->nb.nf_fbd_regs.recmema; + sp->rank = (recmema >> 8) & RANK_MASK; + sp->bank = (recmema >> 12) & BANK_MASK; + sp->cas = (recmemb >> 16) & CAS_MASK; + sp->ras = recmemb & RAS_MASK; + } + intr = "nb.fbd.ma"; /* memory alert */ + } else if (ferr_nf_fbd & ERR_NF_FBD_MA) { /* M14, M15 and M21 */ + intr = "nb.fbd.ch"; /* FBD on channel */ + } else if ((ferr_nf_fbd & ERR_NF_FBD_ECC_CE) != 0) { + /* correctable ECC M17-M20 */ + recmema = rp->nb.nf_fbd_regs.recmema; + recmemb = rp->nb.nf_fbd_regs.recmemb; + sp->rank = (recmema >> 8) & RANK_MASK; + redmemb = rp->nb.nf_fbd_regs.redmemb; + ecc_locator = redmemb & 0x3ffff; + if (ecc_locator & 0x1ff) + sp->channel = branch << 1; + else if (ecc_locator & 0x3fe00) + sp->channel = (branch << 1) + 1; + sp->dimm = sp->rank >> 1; + sp->bank = (recmema >> 12) & BANK_MASK; + sp->cas = (recmemb >> 16) & CAS_MASK; + sp->ras = recmemb & RAS_MASK; + intr = "nb.mem_ce"; + } else if ((ferr_nf_fbd & ERR_NF_FBD_SPARE) != 0) { + /* spare dimm M27, M28 */ + intr = "nb.mem_ds"; + sp->channel = -1; + if (rp->nb.nf_fbd_regs.spcps & SPCPS_SPARE_DEPLOYED) { + sp->rank = + SPCPS_FAILED_RANK(rp->nb.nf_fbd_regs.spcps); + nb_used_spare_rank(sp->branch, sp->rank); + nb_config_gen++; + } + } + } + if (sp->ras != -1) { + sp->pa = dimm_getphys(sp->branch, sp->rank, sp->bank, sp->ras, + sp->cas); + sp->offset = dimm_getoffset(sp->branch, sp->rank, sp->bank, + sp->ras, sp->cas); + } + return (intr); +} + +static struct mch_error_code fat_int_error_code[] = { + { 8, EMASK_INT_B8, ERR_FAT_INT_B8 }, + { 4, EMASK_INT_B4, ERR_FAT_INT_B4 }, + { 3, EMASK_INT_B3, ERR_FAT_INT_B3 }, + { 2, EMASK_INT_B2, ERR_FAT_INT_B2 }, + { 1, EMASK_INT_B1, ERR_FAT_INT_B1 } +}; + +static struct mch_error_code nf_int_error_code[] = { + { 7, EMASK_INT_B7, ERR_NF_INT_B7 }, + { 6, EMASK_INT_B6, ERR_NF_INT_B6 }, + { 5, EMASK_INT_B5, ERR_NF_INT_B5 } +}; + +static int +intel_int_err(uint8_t ferr_fat_int, uint8_t ferr_nf_int) +{ + int rt = -1; + int nerr = 0; + uint8_t emask_int = 0; + int i; + int sz; + + sz = sizeof (fat_int_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (ferr_fat_int & fat_int_error_code[i].error_bit) { + rt = fat_int_error_code[i].intel_error_list; + emask_int |= fat_int_error_code[i].emask; + nerr++; + } + } + + sz = sizeof (nf_int_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (ferr_nf_int & nf_int_error_code[i].error_bit) { + rt = nf_int_error_code[i].intel_error_list; + emask_int |= nf_int_error_code[i].emask; + nerr++; + } + } + + if (emask_int) + nb_int_mask_mc(emask_int); + if (nerr > 1) + rt = -1; + return (rt); +} + +static void +log_int_err(nb_regs_t *rp, int *interpose) +{ + int t = 0; + + rp->flag = NB_REG_LOG_INT; + rp->nb.int_regs.ferr_fat_int = FERR_FAT_INT_RD(interpose); + rp->nb.int_regs.ferr_nf_int = FERR_NF_INT_RD(&t); + *interpose |= t; + rp->nb.int_regs.nerr_fat_int = NERR_FAT_INT_RD(&t); + *interpose |= t; + rp->nb.int_regs.nerr_nf_int = NERR_NF_INT_RD(&t); + *interpose |= t; + rp->nb.int_regs.nrecint = NRECINT_RD(); + rp->nb.int_regs.recint = RECINT_RD(); + rp->nb.int_regs.nrecsf = NRECSF_RD(); + rp->nb.int_regs.recsf = RECSF_RD(); + + if (rp->nb.int_regs.ferr_fat_int || *interpose) + FERR_FAT_INT_WR(rp->nb.int_regs.ferr_fat_int); + if (rp->nb.int_regs.ferr_nf_int || *interpose) + FERR_NF_INT_WR(rp->nb.int_regs.ferr_nf_int); + if (rp->nb.int_regs.nerr_fat_int) + NERR_FAT_INT_WR(rp->nb.int_regs.nerr_fat_int); + if (rp->nb.int_regs.nerr_nf_int) + NERR_NF_INT_WR(rp->nb.int_regs.nerr_nf_int); + /* if interpose write read-only registers to clear from pcii cache */ + if (*interpose) { + NRECINT_WR(); + RECINT_WR(); + NRECSF_WR(); + RECSF_WR(); + } +} + +static void +log_dma_err(nb_regs_t *rp, int *interpose) +{ + rp->flag = NB_REG_LOG_DMA; + + rp->nb.dma_regs.pcists = PCISTS_RD(interpose); + rp->nb.dma_regs.pexdevsts = PCIDEVSTS_RD(); + + if (rp->nb.dma_regs.pcists) + PCISTS_WR(rp->nb.dma_regs.pcists); + if (rp->nb.dma_regs.pexdevsts) + PCIDEVSTS_WR(rp->nb.dma_regs.pexdevsts); +} + +static int +intel_fsb_err(int fsb, uint8_t ferr_fat_fsb, uint8_t ferr_nf_fsb) +{ + int rt = -1; + int nerr = 0; + uint16_t emask_fsb = 0; + + if (ferr_fat_fsb & ERR_FAT_FSB_F9) { + rt = 9; + emask_fsb |= EMASK_FSB_F9; + nerr++; + } + if (ferr_fat_fsb & ERR_FAT_FSB_F2) { + rt = 2; + emask_fsb |= EMASK_FSB_F2; + nerr++; + } + if (ferr_fat_fsb & ERR_FAT_FSB_F1) { + rt = 1; + emask_fsb |= EMASK_FSB_F1; + nerr++; + } + if (ferr_nf_fsb & ERR_NF_FSB_F8) { + rt = 8; + emask_fsb |= EMASK_FSB_F8; + nerr++; + } + if (ferr_nf_fsb & ERR_NF_FSB_F7) { + rt = 7; + emask_fsb |= EMASK_FSB_F7; + nerr++; + } + if (ferr_nf_fsb & ERR_NF_FSB_F6) { + rt = 6; + emask_fsb |= EMASK_FSB_F6; + nerr++; + } + if (emask_fsb) + nb_fsb_mask_mc(fsb, emask_fsb); + if (nerr > 1) + rt = -1; + return (rt); +} + +static void +log_fsb_err(uint64_t ferr, nb_regs_t *rp, int *interpose) +{ + uint8_t fsb; + int t = 0; + + fsb = GE_FERR_FSB(ferr); + rp->flag = NB_REG_LOG_FSB; + + rp->nb.fsb_regs.fsb = fsb; + rp->nb.fsb_regs.ferr_fat_fsb = FERR_FAT_FSB_RD(fsb, interpose); + rp->nb.fsb_regs.ferr_nf_fsb = FERR_NF_FSB_RD(fsb, &t); + *interpose |= t; + rp->nb.fsb_regs.nerr_fat_fsb = NERR_FAT_FSB_RD(fsb, &t); + *interpose |= t; + rp->nb.fsb_regs.nerr_nf_fsb = NERR_NF_FSB_RD(fsb, &t); + *interpose |= t; + rp->nb.fsb_regs.nrecfsb = NRECFSB_RD(fsb); + rp->nb.fsb_regs.nrecfsb_addr = NRECADDR_RD(fsb); + rp->nb.fsb_regs.recfsb = RECFSB_RD(fsb); + if (rp->nb.fsb_regs.ferr_fat_fsb || *interpose) + FERR_FAT_FSB_WR(fsb, rp->nb.fsb_regs.ferr_fat_fsb); + if (rp->nb.fsb_regs.ferr_nf_fsb || *interpose) + FERR_NF_FSB_WR(fsb, rp->nb.fsb_regs.ferr_nf_fsb); + /* if interpose write read-only registers to clear from pcii cache */ + if (*interpose) { + NRECFSB_WR(fsb); + NRECADDR_WR(fsb); + RECFSB_WR(fsb); + } +} + +static struct mch_error_code fat_pex_error_code[] = { + { 19, EMASK_UNCOR_PEX_IO19, PEX_FAT_IO19 }, + { 18, EMASK_UNCOR_PEX_IO18, PEX_FAT_IO18 }, + { 10, EMASK_UNCOR_PEX_IO10, PEX_FAT_IO10 }, + { 9, EMASK_UNCOR_PEX_IO9, PEX_FAT_IO9 }, + { 8, EMASK_UNCOR_PEX_IO8, PEX_FAT_IO8 }, + { 7, EMASK_UNCOR_PEX_IO7, PEX_FAT_IO7 }, + { 6, EMASK_UNCOR_PEX_IO6, PEX_FAT_IO6 }, + { 5, EMASK_UNCOR_PEX_IO5, PEX_FAT_IO5 }, + { 4, EMASK_UNCOR_PEX_IO4, PEX_FAT_IO4 }, + { 3, EMASK_UNCOR_PEX_IO3, PEX_FAT_IO3 }, + { 2, EMASK_UNCOR_PEX_IO2, PEX_FAT_IO2 }, + { 0, EMASK_UNCOR_PEX_IO0, PEX_FAT_IO0 } +}; + +static struct mch_error_code fat_rp_error_code[] = { + { 1, EMASK_RP_PEX_IO1, PEX_FAT_IO1 } +}; + +static struct mch_error_code uncor_pex_error_code[] = { + { 19, EMASK_UNCOR_PEX_IO19, PEX_NF_IO19 }, + { 9, EMASK_UNCOR_PEX_IO9, PEX_NF_IO9 }, + { 8, EMASK_UNCOR_PEX_IO8, PEX_NF_IO8 }, + { 7, EMASK_UNCOR_PEX_IO7, PEX_NF_IO7 }, + { 6, EMASK_UNCOR_PEX_IO6, PEX_NF_IO6 }, + { 5, EMASK_UNCOR_PEX_IO5, PEX_NF_IO5 }, + { 4, EMASK_UNCOR_PEX_IO4, PEX_NF_IO4 }, + { 3, EMASK_UNCOR_PEX_IO3, PEX_NF_IO3 }, + { 0, EMASK_UNCOR_PEX_IO0, PEX_NF_IO0 } +}; + +static struct mch_error_code cor_pex_error_code[] = { + { 16, EMASK_COR_PEX_IO16, PEX_NF_IO16 }, + { 15, EMASK_COR_PEX_IO15, PEX_NF_IO15 }, + { 14, EMASK_COR_PEX_IO14, PEX_NF_IO14 }, + { 13, EMASK_COR_PEX_IO13, PEX_NF_IO13 }, + { 12, EMASK_COR_PEX_IO12, PEX_NF_IO12 }, + { 10, 0, PEX_NF_IO10 }, + { 2, 0, PEX_NF_IO2 } +}; + +static struct mch_error_code rp_pex_error_code[] = { + { 17, EMASK_RP_PEX_IO17, PEX_NF_IO17 }, + { 11, EMASK_RP_PEX_IO11, PEX_NF_IO11 }, +}; + +static int +intel_pex_err(uint32_t pex_fat, uint32_t pex_nf_cor) +{ + int rt = -1; + int nerr = 0; + int i; + int sz; + + sz = sizeof (fat_pex_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (pex_fat & fat_pex_error_code[i].error_bit) { + rt = fat_pex_error_code[i].intel_error_list; + nerr++; + } + } + sz = sizeof (fat_rp_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (pex_fat & fat_rp_error_code[i].error_bit) { + rt = fat_rp_error_code[i].intel_error_list; + nerr++; + } + } + sz = sizeof (uncor_pex_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (pex_nf_cor & uncor_pex_error_code[i].error_bit) { + rt = uncor_pex_error_code[i].intel_error_list; + nerr++; + } + } + + sz = sizeof (cor_pex_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (pex_nf_cor & cor_pex_error_code[i].error_bit) { + rt = cor_pex_error_code[i].intel_error_list; + nerr++; + } + } + sz = sizeof (rp_pex_error_code) / sizeof (struct mch_error_code); + + for (i = 0; i < sz; i++) { + if (pex_nf_cor & rp_pex_error_code[i].error_bit) { + rt = rp_pex_error_code[i].intel_error_list; + nerr++; + } + } + + if (nerr > 1) + rt = -1; + return (rt); +} +static void +log_pex_err(uint64_t ferr, nb_regs_t *rp, int *interpose) +{ + uint8_t pex = (uint8_t)-1; + int t = 0; + + rp->flag = NB_REG_LOG_PEX; + pex = GE_ERR_PEX(ferr); + + rp->nb.pex_regs.pex = pex; + rp->nb.pex_regs.pex_fat_ferr = PEX_FAT_FERR_RD(pex, interpose); + rp->nb.pex_regs.pex_fat_nerr = PEX_FAT_NERR_RD(pex, &t); + *interpose |= t; + rp->nb.pex_regs.pex_nf_corr_ferr = PEX_NF_FERR_RD(pex, &t); + *interpose |= t; + rp->nb.pex_regs.pex_nf_corr_nerr = PEX_NF_NERR_RD(pex, &t); + *interpose |= t; + rp->nb.pex_regs.uncerrsev = UNCERRSEV_RD(pex); + rp->nb.pex_regs.rperrsts = RPERRSTS_RD(pex); + rp->nb.pex_regs.rperrsid = RPERRSID_RD(pex); + if (pex != (uint8_t)-1) + rp->nb.pex_regs.uncerrsts = UNCERRSTS_RD(pex); + else + rp->nb.pex_regs.uncerrsts = 0; + rp->nb.pex_regs.aerrcapctrl = AERRCAPCTRL_RD(pex); + rp->nb.pex_regs.corerrsts = CORERRSTS_RD(pex); + rp->nb.pex_regs.pexdevsts = PEXDEVSTS_RD(pex); + + if (rp->nb.pex_regs.pex_fat_ferr || *interpose) + PEX_FAT_FERR_WR(pex, rp->nb.pex_regs.pex_fat_ferr); + if (rp->nb.pex_regs.pex_fat_nerr) + PEX_FAT_NERR_WR(pex, rp->nb.pex_regs.pex_fat_nerr); + if (rp->nb.pex_regs.pex_nf_corr_ferr || *interpose) + PEX_NF_FERR_WR(pex, rp->nb.pex_regs.pex_nf_corr_ferr); + if (rp->nb.pex_regs.pex_nf_corr_nerr) + PEX_NF_NERR_WR(pex, rp->nb.pex_regs.pex_nf_corr_nerr); + if (rp->nb.pex_regs.uncerrsts || *interpose) + UNCERRSTS_WR(pex, rp->nb.pex_regs.uncerrsts); + if (rp->nb.pex_regs.pexdevsts || *interpose) + PEXDEVSTS_WR(pex, rp->nb.pex_regs.pexdevsts); + if (rp->nb.pex_regs.rperrsts || *interpose) + RPERRSTS_WR(pex, rp->nb.pex_regs.rperrsts); + if (*interpose) + PEXDEVSTS_WR(pex, 0); +} + +static void +log_fat_fbd_err(nb_regs_t *rp, int *interpose) +{ + int channel, branch; + int t = 0; + + rp->flag = NB_REG_LOG_FAT_FBD; + rp->nb.fat_fbd_regs.ferr_fat_fbd = FERR_FAT_FBD_RD(interpose); + channel = (rp->nb.fat_fbd_regs.ferr_fat_fbd >> 28) & 3; + branch = channel >> 1; + rp->nb.fat_fbd_regs.nerr_fat_fbd = NERR_FAT_FBD_RD(&t); + *interpose |= t; + rp->nb.fat_fbd_regs.nrecmema = NRECMEMA_RD(); + rp->nb.fat_fbd_regs.nrecmemb = NRECMEMB_RD(); + rp->nb.fat_fbd_regs.nrecfglog = NRECFGLOG_RD(); + rp->nb.fat_fbd_regs.nrecfbda = NRECFBDA_RD(); + rp->nb.fat_fbd_regs.nrecfbdb = NRECFBDB_RD(); + rp->nb.fat_fbd_regs.nrecfbdc = NRECFBDC_RD(); + rp->nb.fat_fbd_regs.nrecfbdd = NRECFBDD_RD(); + rp->nb.fat_fbd_regs.nrecfbde = NRECFBDE_RD(); + rp->nb.fat_fbd_regs.spcps = SPCPS_RD(branch); + rp->nb.fat_fbd_regs.spcpc = SPCPC_RD(branch); + rp->nb.fat_fbd_regs.uerrcnt = UERRCNT_RD(branch); + rp->nb.fat_fbd_regs.uerrcnt_last = uerrcnt[branch]; + uerrcnt[branch] = rp->nb.fat_fbd_regs.uerrcnt; + rp->nb.fat_fbd_regs.badrama = BADRAMA_RD(branch); + rp->nb.fat_fbd_regs.badramb = BADRAMB_RD(branch); + rp->nb.fat_fbd_regs.badcnt = BADCNT_RD(branch); + if (rp->nb.fat_fbd_regs.ferr_fat_fbd || *interpose) + FERR_FAT_FBD_WR(rp->nb.fat_fbd_regs.ferr_fat_fbd); + if (rp->nb.fat_fbd_regs.nerr_fat_fbd) + NERR_FAT_FBD_WR(rp->nb.fat_fbd_regs.nerr_fat_fbd); + /* if interpose write read-only registers to clear from pcii cache */ + if (*interpose) { + NRECMEMA_WR(); + NRECMEMB_WR(); + NRECFGLOG_WR(); + NRECFBDA_WR(); + NRECFBDB_WR(); + NRECFBDC_WR(); + NRECFBDD_WR(); + NRECFBDE_WR(); + } +} + +static void +log_nf_fbd_err(nb_regs_t *rp, int *interpose) +{ + int channel, branch; + int t = 0; + + rp->flag = NB_REG_LOG_NF_FBD; + rp->nb.nf_fbd_regs.ferr_nf_fbd = FERR_NF_FBD_RD(interpose); + channel = (rp->nb.nf_fbd_regs.ferr_nf_fbd >> 28) & 3; + branch = channel >> 1; + rp->nb.nf_fbd_regs.nerr_nf_fbd = NERR_NF_FBD_RD(&t); + *interpose |= t; + rp->nb.nf_fbd_regs.redmemb = REDMEMB_RD(); + rp->nb.nf_fbd_regs.recmema = RECMEMA_RD(); + rp->nb.nf_fbd_regs.recmemb = RECMEMB_RD(); + rp->nb.nf_fbd_regs.recfglog = RECFGLOG_RD(); + rp->nb.nf_fbd_regs.recfbda = RECFBDA_RD(); + rp->nb.nf_fbd_regs.recfbdb = RECFBDB_RD(); + rp->nb.nf_fbd_regs.recfbdc = RECFBDC_RD(); + rp->nb.nf_fbd_regs.recfbdd = RECFBDD_RD(); + rp->nb.nf_fbd_regs.recfbde = RECFBDE_RD(); + rp->nb.nf_fbd_regs.spcps = SPCPS_RD(branch); + rp->nb.nf_fbd_regs.spcpc = SPCPC_RD(branch); + rp->nb.nf_fbd_regs.cerrcnt = UERRCNT_RD(branch); + rp->nb.nf_fbd_regs.cerrcnt_last = cerrcnt[branch]; + cerrcnt[branch] = rp->nb.nf_fbd_regs.cerrcnt; + rp->nb.nf_fbd_regs.badrama = BADRAMA_RD(branch); + rp->nb.nf_fbd_regs.badramb = BADRAMB_RD(branch); + rp->nb.nf_fbd_regs.badcnt = BADCNT_RD(branch); + if (rp->nb.nf_fbd_regs.ferr_nf_fbd || *interpose) + FERR_NF_FBD_WR(rp->nb.nf_fbd_regs.ferr_nf_fbd); + if (rp->nb.nf_fbd_regs.nerr_nf_fbd) + NERR_NF_FBD_WR(rp->nb.nf_fbd_regs.nerr_nf_fbd); + /* if interpose write read-only registers to clear from pcii cache */ + if (*interpose) { + RECMEMA_WR(); + RECMEMB_WR(); + RECFGLOG_WR(); + RECFBDA_WR(); + RECFBDB_WR(); + RECFBDC_WR(); + RECFBDD_WR(); + RECFBDE_WR(); + SPCPS_WR(branch); + } +} + +static void +log_ferr(uint64_t ferr, uint32_t *nerrp, nb_logout_t *log, int willpanic) +{ + nb_regs_t *rp = &log->nb_regs; + uint32_t nerr = *nerrp; + int interpose = 0; + + log->acl_timestamp = gethrtime_waitfree(); + if ((ferr & (GE_PCIEX_FATAL | GE_PCIEX_NF)) != 0) { + log_pex_err(ferr, rp, &interpose); + *nerrp = nerr & ~(GE_PCIEX_FATAL | GE_PCIEX_NF); + } else if ((ferr & GE_FBD_FATAL) != 0) { + log_fat_fbd_err(rp, &interpose); + *nerrp = nerr & ~GE_NERR_FBD_FATAL; + } else if ((ferr & GE_FBD_NF) != 0) { + log_nf_fbd_err(rp, &interpose); + *nerrp = nerr & ~GE_NERR_FBD_NF; + } else if ((ferr & (GE_FERR_FSB_FATAL | GE_FERR_FSB_NF)) != 0) { + log_fsb_err(ferr, rp, &interpose); + *nerrp = nerr & ~(GE_NERR_FSB_FATAL | GE_NERR_FSB_NF); + } else if ((ferr & (GE_DMA_FATAL | GE_DMA_NF)) != 0) { + log_dma_err(rp, &interpose); + *nerrp = nerr & ~(GE_DMA_FATAL | GE_DMA_NF); + } else if ((ferr & (GE_INT_FATAL | GE_INT_NF)) != 0) { + log_int_err(rp, &interpose); + *nerrp = nerr & ~(GE_INT_FATAL | GE_INT_NF); + } + if (interpose) + log->type = "inject"; + else + log->type = "error"; + errorq_dispatch(nb_queue, log, sizeof (nb_logout_t), + willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC); +} + +static void +log_nerr(uint32_t *errp, nb_logout_t *log, int willpanic) +{ + uint32_t err; + nb_regs_t *rp = &log->nb_regs; + int interpose = 0; + + err = *errp; + log->acl_timestamp = gethrtime_waitfree(); + if ((err & (GE_PCIEX_FATAL | GE_PCIEX_NF)) != 0) { + log_pex_err(err, rp, &interpose); + *errp = err & ~(GE_PCIEX_FATAL | GE_PCIEX_NF); + } else if ((err & GE_NERR_FBD_FATAL) != 0) { + log_fat_fbd_err(rp, &interpose); + *errp = err & ~GE_NERR_FBD_FATAL; + } else if ((err & GE_NERR_FBD_NF) != 0) { + log_nf_fbd_err(rp, &interpose); + *errp = err & ~GE_NERR_FBD_NF; + } else if ((err & (GE_NERR_FSB_FATAL | GE_NERR_FSB_NF)) != 0) { + log_fsb_err(GE_NERR_TO_FERR_FSB(err), rp, &interpose); + *errp = err & ~(GE_NERR_FSB_FATAL | GE_NERR_FSB_NF); + } else if ((err & (GE_DMA_FATAL | GE_DMA_NF)) != 0) { + log_dma_err(rp, &interpose); + *errp = err & ~(GE_DMA_FATAL | GE_DMA_NF); + } else if ((err & (GE_INT_FATAL | GE_INT_NF)) != 0) { + log_int_err(rp, &interpose); + *errp = err & ~(GE_INT_FATAL | GE_INT_NF); + } + if (interpose) + log->type = "inject"; + else + log->type = "error"; + errorq_dispatch(nb_queue, log, sizeof (nb_logout_t), + willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC); +} + +/*ARGSUSED*/ +void +nb_error_trap(cmi_hdl_t hdl, boolean_t ismc, boolean_t willpanic) +{ + uint64_t ferr; + uint32_t nerr, err; + int nmc = 0; + int i; + + if (mutex_tryenter(&nb_mutex) == 0) + return; + + nerr = NERR_GLOBAL_RD(); + err = nerr; + for (i = 0; i < NB_MAX_ERRORS; i++) { + ferr = FERR_GLOBAL_RD(); + nb_log.nb_regs.chipset = nb_chipset; + nb_log.nb_regs.ferr = ferr; + nb_log.nb_regs.nerr = nerr; + if (ferr) { + log_ferr(ferr, &err, &nb_log, willpanic); + FERR_GLOBAL_WR(ferr); + nmc++; + } else if (err) { + log_nerr(&err, &nb_log, willpanic); + nmc++; + } + } + if (nerr) { + NERR_GLOBAL_WR(nerr); + } + if (nmc == 0 && nb_mask_mc_set) + nb_mask_mc_reset(); + mutex_exit(&nb_mutex); +} + +static void +nb_fsb_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload, + nb_scatchpad_t *data) +{ + int intel_error_list; + char buf[32]; + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FSB, + DATA_TYPE_UINT8, nb_regs->nb.fsb_regs.fsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_FAT_FSB, + DATA_TYPE_UINT8, nb_regs->nb.fsb_regs.ferr_fat_fsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_FAT_FSB, + DATA_TYPE_UINT8, nb_regs->nb.fsb_regs.nerr_fat_fsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_NF_FSB, + DATA_TYPE_UINT8, nb_regs->nb.fsb_regs.ferr_nf_fsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_NF_FSB, + DATA_TYPE_UINT8, nb_regs->nb.fsb_regs.nerr_nf_fsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFSB, + DATA_TYPE_UINT32, nb_regs->nb.fsb_regs.nrecfsb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFSB_ADDR, + DATA_TYPE_UINT64, nb_regs->nb.fsb_regs.nrecfsb_addr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFSB, + DATA_TYPE_UINT32, nb_regs->nb.fsb_regs.recfsb, NULL); + intel_error_list = data->intel_error_list; + if (intel_error_list >= 0) + (void) snprintf(buf, sizeof (buf), "F%d", intel_error_list); + else + (void) snprintf(buf, sizeof (buf), "Multiple or unknown error"); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ERROR_NO, + DATA_TYPE_STRING, buf, NULL); +} + +static void +nb_pex_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload, + nb_scatchpad_t *data) +{ + int intel_error_list; + char buf[32]; + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEX, + DATA_TYPE_UINT8, nb_regs->nb.pex_regs.pex, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEX_FAT_FERR, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.pex_fat_ferr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEX_FAT_NERR, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.pex_fat_nerr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEX_NF_CORR_FERR, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.pex_nf_corr_ferr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEX_NF_CORR_NERR, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.pex_nf_corr_nerr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_UNCERRSEV, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.uncerrsev, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RPERRSTS, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.rperrsts, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RPERRSID, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.rperrsid, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_UNCERRSTS, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.uncerrsts, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_AERRCAPCTRL, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.aerrcapctrl, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_CORERRSTS, + DATA_TYPE_UINT32, nb_regs->nb.pex_regs.corerrsts, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEXDEVSTS, + DATA_TYPE_UINT16, nb_regs->nb.pex_regs.pexdevsts, NULL); + intel_error_list = data->intel_error_list; + if (intel_error_list >= 0) + (void) snprintf(buf, sizeof (buf), "IO%d", intel_error_list); + else + (void) snprintf(buf, sizeof (buf), "Multiple or unknown error"); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ERROR_NO, + DATA_TYPE_STRING, buf, NULL); +} + +static void +nb_int_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload, + nb_scatchpad_t *data) +{ + int intel_error_list; + char buf[32]; + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_FAT_INT, + DATA_TYPE_UINT8, nb_regs->nb.int_regs.ferr_fat_int, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_NF_INT, + DATA_TYPE_UINT8, nb_regs->nb.int_regs.ferr_nf_int, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_FAT_INT, + DATA_TYPE_UINT8, nb_regs->nb.int_regs.nerr_fat_int, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_NF_INT, + DATA_TYPE_UINT8, nb_regs->nb.int_regs.nerr_nf_int, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECINT, + DATA_TYPE_UINT32, nb_regs->nb.int_regs.nrecint, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECINT, + DATA_TYPE_UINT32, nb_regs->nb.int_regs.recint, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECSF, + DATA_TYPE_UINT64, nb_regs->nb.int_regs.nrecsf, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECSF, + DATA_TYPE_UINT64, nb_regs->nb.int_regs.recsf, NULL); + intel_error_list = data->intel_error_list; + if (intel_error_list >= 0) + (void) snprintf(buf, sizeof (buf), "B%d", intel_error_list); + else + (void) snprintf(buf, sizeof (buf), "Multiple or unknown error"); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ERROR_NO, + DATA_TYPE_STRING, buf, NULL); +} + +static void +nb_fat_fbd_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload, + nb_scatchpad_t *data) +{ + nb_mem_scatchpad_t *sp; + char buf[32]; + + sp = &((nb_scatchpad_t *)data)->ms; + + if (sp->ras != -1) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK, + DATA_TYPE_INT32, sp->bank, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_CAS, + DATA_TYPE_INT32, sp->cas, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RAS, + DATA_TYPE_INT32, sp->ras, NULL); + if (sp->offset != -1LL) { + fm_payload_set(payload, FM_FMRI_MEM_OFFSET, + DATA_TYPE_UINT64, sp->offset, NULL); + } + if (sp->pa != -1LL) { + fm_payload_set(payload, FM_FMRI_MEM_PHYSADDR, + DATA_TYPE_UINT64, sp->pa, NULL); + } + } + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_FAT_FBD, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.ferr_fat_fbd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_FAT_FBD, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nerr_fat_fbd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECMEMA, + DATA_TYPE_UINT16, nb_regs->nb.fat_fbd_regs.nrecmema, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECMEMB, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecmemb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFGLOG, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfglog, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFBDA, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfbda, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFBDB, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfbdb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFBDC, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfbdc, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFBDD, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfbdd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NRECFBDE, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.nrecfbde, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SPCPS, + DATA_TYPE_UINT8, nb_regs->nb.fat_fbd_regs.spcps, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SPCPC, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.spcpc, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_UERRCNT, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.uerrcnt, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_UERRCNT_LAST, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.uerrcnt_last, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADRAMA, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.badrama, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADRAMB, + DATA_TYPE_UINT16, nb_regs->nb.fat_fbd_regs.badramb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADCNT, + DATA_TYPE_UINT32, nb_regs->nb.fat_fbd_regs.badcnt, NULL); + + if (sp->intel_error_list >= 0) + (void) snprintf(buf, sizeof (buf), "M%d", sp->intel_error_list); + else + (void) snprintf(buf, sizeof (buf), "Multiple or unknown error"); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ERROR_NO, + DATA_TYPE_STRING, buf, NULL); +} + +static void +nb_nf_fbd_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload, + nb_scatchpad_t *data) +{ + nb_mem_scatchpad_t *sp; + char buf[32]; + + sp = &((nb_scatchpad_t *)data)->ms; + + if (sp->dimm == -1 && sp->rank != -1) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RANK, + DATA_TYPE_INT32, sp->rank, NULL); + } + if (sp->ras != -1) { + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BANK, + DATA_TYPE_INT32, sp->bank, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_CAS, + DATA_TYPE_INT32, sp->cas, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RAS, + DATA_TYPE_INT32, sp->ras, NULL); + if (sp->offset != -1LL) { + fm_payload_set(payload, FM_FMRI_MEM_OFFSET, + DATA_TYPE_UINT64, sp->offset, NULL); + } + if (sp->pa != -1LL) { + fm_payload_set(payload, FM_FMRI_MEM_PHYSADDR, + DATA_TYPE_UINT64, sp->pa, NULL); + } + } + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_NF_FBD, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.ferr_nf_fbd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_NF_FBD, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.nerr_nf_fbd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECMEMA, + DATA_TYPE_UINT16, nb_regs->nb.nf_fbd_regs.recmema, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECMEMB, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recmemb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFGLOG, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfglog, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFBDA, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfbda, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFBDB, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfbdb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFBDC, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfbdc, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFBDD, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfbdd, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RECFBDE, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.recfbde, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SPCPS, + DATA_TYPE_UINT8, nb_regs->nb.nf_fbd_regs.spcps, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_SPCPC, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.spcpc, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_CERRCNT, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.cerrcnt, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_CERRCNT_LAST, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.cerrcnt_last, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADRAMA, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.badrama, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADRAMB, + DATA_TYPE_UINT16, nb_regs->nb.nf_fbd_regs.badramb, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_BADCNT, + DATA_TYPE_UINT32, nb_regs->nb.nf_fbd_regs.badcnt, NULL); + + if (sp->intel_error_list >= 0) + (void) snprintf(buf, sizeof (buf), "M%d", sp->intel_error_list); + else + (void) snprintf(buf, sizeof (buf), "Multiple or unknown error"); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_ERROR_NO, + DATA_TYPE_STRING, buf, NULL); +} + +static void +nb_dma_err_payload(const nb_regs_t *nb_regs, nvlist_t *payload) +{ + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PCISTS, + DATA_TYPE_UINT16, nb_regs->nb.dma_regs.pcists, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_PEXDEVSTS, + DATA_TYPE_UINT16, nb_regs->nb.dma_regs.pexdevsts, NULL); +} + +static void +nb_ereport_add_logout(nvlist_t *payload, const nb_logout_t *acl, + nb_scatchpad_t *data) +{ + const nb_regs_t *nb_regs = &acl->nb_regs; + + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_MC_TYPE, + DATA_TYPE_STRING, acl->type); + switch (nb_regs->flag) { + case NB_REG_LOG_FSB: + nb_fsb_err_payload(nb_regs, payload, data); + break; + case NB_REG_LOG_PEX: + nb_pex_err_payload(nb_regs, payload, data); + break; + case NB_REG_LOG_INT: + nb_int_err_payload(nb_regs, payload, data); + break; + case NB_REG_LOG_FAT_FBD: + nb_fat_fbd_err_payload(nb_regs, payload, data); + break; + case NB_REG_LOG_NF_FBD: + nb_nf_fbd_err_payload(nb_regs, payload, data); + break; + case NB_REG_LOG_DMA: + nb_dma_err_payload(nb_regs, payload); + break; + default: + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_FERR_GLOBAL, + DATA_TYPE_UINT64, nb_regs->ferr, NULL); + fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_NERR_GLOBAL, + DATA_TYPE_UINT32, nb_regs->nerr, NULL); + break; + } +} + +void +nb_fsb_report(const nb_regs_t *nb_regs, char *class, nvlist_t *detector, + nb_scatchpad_t *data) +{ + int chip; + + if (nb_chipset == INTEL_NB_7300) + chip = nb_regs->nb.fsb_regs.fsb * 2; + else + chip = nb_regs->nb.fsb_regs.fsb; + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 2, + "motherboard", 0, "chip", chip); + + data->intel_error_list = intel_fsb_err(nb_regs->nb.fsb_regs.fsb, + nb_regs->nb.fsb_regs.ferr_fat_fsb, + nb_regs->nb.fsb_regs.ferr_nf_fsb); + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "fsb"); +} + +void +nb_pex_report(const nb_regs_t *nb_regs, char *class, nvlist_t *detector, + nb_scatchpad_t *data) +{ + int hostbridge; + + + if (nb_regs->nb.pex_regs.pex == 0) { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 1, + "motherboard", 0); + } else { + hostbridge = nb_regs->nb.pex_regs.pex - 1; + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 2, + "motherboard", 0, + "hostbridge", hostbridge); + } + + data->intel_error_list = + intel_pex_err(nb_regs->nb.pex_regs.pex_fat_ferr, + nb_regs->nb.pex_regs.pex_nf_corr_ferr); + + if (nb_regs->nb.pex_regs.pex == 0) { + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "esi"); + } else { + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "pex"); + } +} + +void +nb_int_report(const nb_regs_t *nb_regs, char *class, nvlist_t *detector, + void *data) +{ + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 1, + "motherboard", 0); + + ((nb_scatchpad_t *)data)->intel_error_list = + intel_int_err(nb_regs->nb.int_regs.ferr_fat_int, + nb_regs->nb.int_regs.ferr_nf_int); + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "ie"); +} + +void +nb_fat_fbd_report(const nb_regs_t *nb_regs, char *class, nvlist_t *detector, + void *data) +{ + char *intr; + nb_mem_scatchpad_t *sp; + + intr = fat_memory_error(nb_regs, data); + sp = &((nb_scatchpad_t *)data)->ms; + + if (sp->dimm != -1) { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 5, + "motherboard", 0, + "memory-controller", sp->branch, + "dram-channel", sp->channel, + "dimm", sp->dimm, + "rank", sp->rank); + } else if (sp->channel != -1) { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 3, + "motherboard", 0, + "memory-controller", sp->branch, + "dram-channel", sp->channel); + } else { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 2, + "motherboard", 0, + "memory-controller", sp->branch); + } + + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, intr); +} + +void +nb_nf_fbd_report(const nb_regs_t *nb_regs, char *class, nvlist_t *detector, + void *data) +{ + char *intr; + nb_mem_scatchpad_t *sp; + + intr = nf_memory_error(nb_regs, data); + sp = &((nb_scatchpad_t *)data)->ms; + + if (sp->dimm != -1) { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 5, + "motherboard", 0, + "memory-controller", sp->branch, + "dram-channel", sp->channel, + "dimm", sp->dimm, + "rank", sp->rank); + } else if (sp->channel != -1) { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 3, + "motherboard", 0, + "memory-controller", sp->branch, + "dram-channel", sp->channel); + } else { + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 2, + "motherboard", 0, + "memory-controller", sp->branch); + } + + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, intr); +} + +void +nb_dma_report(char *class, nvlist_t *detector) +{ + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 1, + "motherboard", 0); + + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "dma"); +} + + +nvlist_t * +nb_report(const nb_regs_t *nb_regs, char *class, nv_alloc_t *nva, void *scratch) +{ + nvlist_t *detector = fm_nvlist_create(nva); + + switch (nb_regs->flag) { + case NB_REG_LOG_FSB: + nb_fsb_report(nb_regs, class, detector, scratch); + break; + case NB_REG_LOG_PEX: + nb_pex_report(nb_regs, class, detector, scratch); + break; + case NB_REG_LOG_INT: + nb_int_report(nb_regs, class, detector, scratch); + break; + case NB_REG_LOG_FAT_FBD: + nb_fat_fbd_report(nb_regs, class, detector, scratch); + break; + case NB_REG_LOG_NF_FBD: + nb_nf_fbd_report(nb_regs, class, detector, scratch); + break; + case NB_REG_LOG_DMA: + nb_dma_report(class, detector); + break; + default: + fm_fmri_hc_set(detector, FM_HC_SCHEME_VERSION, NULL, NULL, 1, + "motherboard", 0); + + (void) snprintf(class, FM_MAX_CLASS, "%s.%s.%s.%s", + FM_ERROR_CPU, FM_EREPORT_CPU_INTEL, "nb", "unknown"); + } + return (detector); +} + +/*ARGSUSED*/ +void +nb_drain(void *ignored, const void *data, const errorq_elem_t *eqe) +{ + nb_logout_t *acl = (nb_logout_t *)data; + errorq_elem_t *eqep, *scr_eqep; + nvlist_t *ereport, *detector; + nv_alloc_t *nva = NULL; + char buf[FM_MAX_CLASS]; + nb_scatchpad_t nb_scatchpad; + + if (panicstr) { + if ((eqep = errorq_reserve(ereport_errorq)) == NULL) + return; + ereport = errorq_elem_nvl(ereport_errorq, eqep); + /* + * Now try to allocate another element for scratch space and + * use that for further scratch space (eg for constructing + * nvlists to add the main ereport). If we can't reserve + * a scratch element just fallback to working within the + * element we already have, and hope for the best. All this + * is necessary because the fixed buffer nv allocator does + * not reclaim freed space and nvlist construction is + * expensive. + */ + if ((scr_eqep = errorq_reserve(ereport_errorq)) != NULL) + nva = errorq_elem_nva(ereport_errorq, scr_eqep); + else + nva = errorq_elem_nva(ereport_errorq, eqep); + } else { + ereport = fm_nvlist_create(NULL); + } + detector = nb_report(&acl->nb_regs, buf, nva, &nb_scatchpad); + if (detector == NULL) + return; + fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, + fm_ena_generate(acl->acl_timestamp, FM_ENA_FMT1), detector, NULL); + /* + * We're done with 'detector' so reclaim the scratch space. + */ + if (panicstr) { + fm_nvlist_destroy(detector, FM_NVA_RETAIN); + nv_alloc_reset(nva); + } else { + fm_nvlist_destroy(detector, FM_NVA_FREE); + } + + /* + * Encode the error-specific data that was saved in the logout area. + */ + nb_ereport_add_logout(ereport, acl, &nb_scatchpad); + + if (panicstr) { + errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); + if (scr_eqep) + errorq_cancel(ereport_errorq, scr_eqep); + } else { + (void) fm_ereport_post(ereport, EVCH_TRYHARD); + fm_nvlist_destroy(ereport, FM_NVA_FREE); + } +} diff --git a/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.conf b/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.conf new file mode 100644 index 0000000000..894e428703 --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/intel_nb5000.conf @@ -0,0 +1,26 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# ident "%Z%%M% %I% %E% SMI" diff --git a/usr/src/uts/i86pc/io/intel_nb5000/intel_nbdrv.c b/usr/src/uts/i86pc/io/intel_nb5000/intel_nbdrv.c new file mode 100644 index 0000000000..7b3daa2596 --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/intel_nbdrv.c @@ -0,0 +1,532 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/time.h> +#include <sys/nvpair.h> +#include <sys/cmn_err.h> +#include <sys/cred.h> +#include <sys/open.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/conf.h> +#include <sys/modctl.h> +#include <sys/cyclic.h> +#include <sys/errorq.h> +#include <sys/stat.h> +#include <sys/cpuvar.h> +#include <sys/mc_intel.h> +#include <sys/mc.h> +#include <sys/fm/protocol.h> +#include "nb_log.h" +#include "nb5000.h" + +char _depends_on[] = "drv/smbios"; + +nvlist_t *inb_mc_nvl; +krwlock_t inb_mc_lock; + +char *inb_mc_snapshot; +uint_t nb_config_gen; +uint_t inb_mc_snapshotgen; +size_t inb_mc_snapshotsz; +static dev_info_t *inb_dip; +int nb_allow_detach = 0; + +static uint64_t +rank_to_base(uint8_t branch, uint8_t rank, uint8_t *interleave, uint64_t *limit, + uint64_t *hole_base, uint64_t *hole_size, uint8_t *wayp, + uint8_t *branch_interleavep) +{ + uint8_t i, j; + uint64_t base = 0; + uint64_t lt = 0; + uint64_t h = 0; + uint64_t hs = 0; + uint8_t il = 1; + uint8_t way = 0; + uint8_t branch_interleave = 0; + + for (i = 0; i < NB_MEM_RANK_SELECT; i++) { + for (j = 0; j < NB_RANKS_IN_SELECT; j++) { + if (nb_ranks[branch][i].rank[j] == rank) { + base = nb_ranks[branch][i].base; + lt = nb_ranks[branch][i].limit; + il = nb_ranks[branch][i].interleave; + h = nb_ranks[branch][i].hole_base; + hs = nb_ranks[branch][i].hole_size; + way = j; + branch_interleave = + nb_ranks[branch][i].branch_interleave; + i = NB_MEM_RANK_SELECT; + break; + } + } + } + if (lt == 0) { + for (i = 0; lt == 0 && i < NB_MEM_BRANCH_SELECT; i++) { + if (nb_banks[i].way[branch] && + base >= nb_banks[i].base && + base < nb_banks[i].base + nb_banks[i].limit) { + lt = nb_banks[i].limit; + break; + } + } + } + *interleave = il; + *limit = lt; + *hole_base = h; + *hole_size = hs; + *wayp = way; + *branch_interleavep = branch_interleave; + return (base); +} + +void +inb_rank(nvlist_t *newdimm, nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) +{ + nvlist_t **newrank; + int i; + + newrank = kmem_zalloc(sizeof (nvlist_t *) * nb_dimm->nranks, KM_SLEEP); + for (i = 0; i < nb_dimm->nranks; i++) { + uint64_t dimm_base; + uint64_t limit; + uint8_t interleave; + uint8_t way; + uint8_t branch_interleave; + uint64_t hole_base; + uint64_t hole_size; + + dimm_base = rank_to_base(channel/2, dimm*2 + i, &interleave, + &limit, &hole_base, &hole_size, &way, &branch_interleave); + (void) nvlist_alloc(&newrank[i], NV_UNIQUE_NAME, KM_SLEEP); + + (void) nvlist_add_uint64(newrank[i], "dimm-rank-base", + dimm_base); + if (hole_size) { + (void) nvlist_add_uint64(newrank[i], "dimm-hole", + hole_base); + (void) nvlist_add_uint64(newrank[i], "dimm-hole-size", + hole_size); + } + (void) nvlist_add_uint64(newrank[i], "dimm-rank-limit", + limit); + if (interleave > 1) { + (void) nvlist_add_uint32(newrank[i], + "dimm-rank-interleave", (uint32_t)interleave); + (void) nvlist_add_uint32(newrank[i], + "dimm-rank-interleave-way", (uint32_t)way); + if (branch_interleave) { + (void) nvlist_add_uint32(newrank[i], + "dimm-rank-interleave-branch", (uint32_t)1); + } + } + } + (void) nvlist_add_nvlist_array(newdimm, MCINTEL_NVLIST_RANKS, newrank, + nb_dimm->nranks); + kmem_free(newrank, sizeof (nvlist_t *) * nb_dimm->nranks); +} + +nvlist_t * +inb_dimm(nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) +{ + nvlist_t *newdimm; + uint8_t t; + char sbuf[65]; + + (void) nvlist_alloc(&newdimm, NV_UNIQUE_NAME, KM_SLEEP); + (void) nvlist_add_uint32(newdimm, "dimm-number", dimm); + + if (nb_dimm->dimm_size >= 1024*1024*1024) { + (void) snprintf(sbuf, sizeof (sbuf), "%dG", + (int)(nb_dimm->dimm_size / (1024*1024*1024))); + } else { + (void) snprintf(sbuf, sizeof (sbuf), "%dM", + (int)(nb_dimm->dimm_size / (1024*1024))); + } + (void) nvlist_add_string(newdimm, "dimm-size", sbuf); + (void) nvlist_add_uint32(newdimm, "nbanks", (uint32_t)nb_dimm->nbanks); + (void) nvlist_add_uint32(newdimm, "ncolumn", + (uint32_t)nb_dimm->ncolumn); + (void) nvlist_add_uint32(newdimm, "nrow", (uint32_t)nb_dimm->nrow); + (void) nvlist_add_uint32(newdimm, "width", (uint32_t)nb_dimm->width); + (void) nvlist_add_uint32(newdimm, "ranks", (uint32_t)nb_dimm->nranks); + inb_rank(newdimm, nb_dimm, channel, dimm); + (void) nvlist_add_uint32(newdimm, "manufacture-id", + (uint32_t)nb_dimm->manufacture_id); + (void) nvlist_add_uint32(newdimm, "manufacture-location", + (uint32_t)nb_dimm->manufacture_location); + (void) nvlist_add_uint32(newdimm, "manufacture-week", + (uint32_t)nb_dimm->manufacture_week); + (void) nvlist_add_uint32(newdimm, "manufacture-year", + (uint32_t)nb_dimm->manufacture_year + 2000); + /* create Sun Serial number from SPD data */ + (void) snprintf(sbuf, sizeof (sbuf), "%04x%02x%02x%02x%08x", + (uint32_t)nb_dimm->manufacture_id & 0x7fff, + (uint32_t)nb_dimm->manufacture_location, + (uint32_t)nb_dimm->manufacture_year, + (uint32_t)nb_dimm->manufacture_week, + nb_dimm->serial_number); + (void) nvlist_add_string(newdimm, FM_FMRI_HC_SERIAL_ID, sbuf); + if (nb_dimm->part_number && nb_dimm->part_number[0]) { + t = sizeof (nb_dimm->part_number); + (void) strncpy(sbuf, nb_dimm->part_number, t); + sbuf[t] = 0; + (void) nvlist_add_string(newdimm, FM_FMRI_HC_PART, sbuf); + } + if (nb_dimm->revision && nb_dimm->revision[0]) { + t = sizeof (nb_dimm->revision); + (void) strncpy(sbuf, nb_dimm->revision, t); + sbuf[t] = 0; + (void) nvlist_add_string(newdimm, FM_FMRI_HC_REVISION, sbuf); + } + t = sizeof (nb_dimm->label); + (void) strncpy(sbuf, nb_dimm->label, t); + sbuf[t] = 0; + (void) nvlist_add_string(newdimm, FM_FAULT_FRU_LABEL, sbuf); + return (newdimm); +} + +static void +inb_dimmlist(nvlist_t *nvl) +{ + nvlist_t **dimmlist; + nvlist_t **newchannel; + int nchannels = nb_number_memory_controllers * 2; + int nd; + uint8_t i, j; + nb_dimm_t **dimmpp; + nb_dimm_t *dimmp; + + dimmlist = kmem_zalloc(sizeof (nvlist_t *) * nb_dimms_per_channel, + KM_SLEEP); + newchannel = kmem_zalloc(sizeof (nvlist_t *) * nchannels, KM_SLEEP); + dimmpp = nb_dimms; + for (i = 0; i < nchannels; i++) { + (void) nvlist_alloc(&newchannel[i], NV_UNIQUE_NAME, KM_SLEEP); + nd = 0; + for (j = 0; j < nb_dimms_per_channel; j++) { + dimmp = *dimmpp; + if (dimmp != NULL) { + dimmlist[nd] = inb_dimm(dimmp, i, (uint32_t)j); + nd++; + } + dimmpp++; + } + if (nd) { + (void) nvlist_add_nvlist_array(newchannel[i], + "memory-dimms", dimmlist, nd); + for (j = 0; j < nd; j++) + nvlist_free(dimmlist[j]); + } + } + (void) nvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_MC, newchannel, + nchannels); + kmem_free(dimmlist, sizeof (nvlist_t *) * nb_dimms_per_channel); + kmem_free(newchannel, sizeof (nvlist_t *) * nchannels); +} + +static char * +inb_mc_name() +{ + char *mc; + + switch (nb_chipset) { + case INTEL_NB_7300: + mc = "Intel 7300"; + break; + case INTEL_NB_5000P: + mc = "Intel 5000P"; + break; + case INTEL_NB_5000V: + mc = "Intel 5000V"; + break; + case INTEL_NB_5000X: + mc = "Intel 5000X"; + break; + case INTEL_NB_5000Z: + mc = "Intel 5000Z"; + break; + default: + mc = "Intel 5000"; + break; + } + return (mc); +} + +static void +inb_create_nvl() +{ + nvlist_t *nvl; + + (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); + (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR, + MCINTEL_NVLIST_VERS); + (void) nvlist_add_string(nvl, "memory-controller", inb_mc_name()); + inb_dimmlist(nvl); + + if (inb_mc_nvl) + nvlist_free(inb_mc_nvl); + inb_mc_nvl = nvl; +} + +static void +inb_mc_snapshot_destroy() +{ + ASSERT(RW_LOCK_HELD(&inb_mc_lock)); + + if (inb_mc_snapshot == NULL) + return; + + kmem_free(inb_mc_snapshot, inb_mc_snapshotsz); + inb_mc_snapshot = NULL; + inb_mc_snapshotsz = 0; + inb_mc_snapshotgen++; +} + +static int +inb_mc_snapshot_update() +{ + ASSERT(RW_LOCK_HELD(&inb_mc_lock)); + + if (inb_mc_snapshot != NULL) + return (0); + + if (nvlist_pack(inb_mc_nvl, &inb_mc_snapshot, &inb_mc_snapshotsz, + NV_ENCODE_XDR, KM_SLEEP) != 0) + return (-1); + + return (0); +} + +/*ARGSUSED*/ +static int +inb_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, + int *rvalp) +{ + int rc = 0; + mc_snapshot_info_t mcs; + + if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT) + return (EINVAL); + + rw_enter(&inb_mc_lock, RW_READER); + if (inb_mc_nvl == NULL || inb_mc_snapshotgen != nb_config_gen) { + if (!rw_tryupgrade(&inb_mc_lock)) { + rw_exit(&inb_mc_lock); + return (EAGAIN); + } + if (inb_mc_nvl) + inb_mc_snapshot_destroy(); + inb_create_nvl(); + nb_config_gen = inb_mc_snapshotgen; + (void) inb_mc_snapshot_update(); + } + switch (cmd) { + case MC_IOC_SNAPSHOT_INFO: + mcs.mcs_size = (uint32_t)inb_mc_snapshotsz; + mcs.mcs_gen = inb_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(inb_mc_snapshot, (void *)arg, inb_mc_snapshotsz, + mode) < 0) + rc = EFAULT; + break; + } + rw_exit(&inb_mc_lock); + return (rc); +} + +/*ARGSUSED*/ +static int +inb_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, + void **result) +{ + if ((infocmd != DDI_INFO_DEVT2DEVINFO && + infocmd != DDI_INFO_DEVT2INSTANCE) || inb_dip == NULL) { + *result = NULL; + return (DDI_FAILURE); + } + if (infocmd == DDI_INFO_DEVT2DEVINFO) + *result = inb_dip; + else + *result = (void *)(uintptr_t)ddi_get_instance(inb_dip); + return (0); +} + +static int +inb_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + if (cmd == DDI_RESUME) { + nb_dev_reinit(); + return (DDI_SUCCESS); + } + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + if (inb_dip == NULL) { + inb_dip = dip; + (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", + inb_mc_name()); + nb_pci_cfg_setup(dip); + if (nb_dev_init()) { + nb_pci_cfg_free(); + inb_dip = NULL; + return (DDI_FAILURE); + } + if (ddi_create_minor_node(dip, "mc-intel", S_IFCHR, 0, + "ddi_mem_ctrl", 0) != DDI_SUCCESS) { + cmn_err(CE_WARN, "failed to create minor node" + " for memory controller\n"); + } + cmi_hdl_walk(inb_mc_register, NULL, NULL, NULL); + } + + return (DDI_SUCCESS); +} + +/*ARGSUSED*/ +static int +inb_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (nb_allow_detach && cmd == DDI_DETACH && dip == inb_dip) { + rw_enter(&inb_mc_lock, RW_WRITER); + inb_mc_snapshot_destroy(); + rw_exit(&inb_mc_lock); + inb_dip = NULL; + return (DDI_SUCCESS); + } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) { + return (DDI_SUCCESS); + } else { + return (DDI_FAILURE); + } +} + +/*ARGSUSED*/ +static int +inb_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) +{ + if (otyp != OTYP_CHR) + return (EINVAL); + + rw_enter(&inb_mc_lock, RW_READER); + if (getminor(*devp) >= 1) { + rw_exit(&inb_mc_lock); + return (EINVAL); + } + rw_exit(&inb_mc_lock); + + return (0); +} + +/*ARGSUSED*/ +static int +inb_mc_close(dev_t dev, int flag, int otyp, cred_t *credp) +{ + return (0); +} + + +static struct cb_ops inb_mc_cb_ops = { + inb_mc_open, + inb_mc_close, + nodev, /* not a block driver */ + nodev, /* no print routine */ + nodev, /* no dump routine */ + nodev, /* no read routine */ + nodev, /* no write routine */ + inb_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 */ +}; + +static struct dev_ops inb_mc_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + inb_mc_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify */ + nulldev, /* devo_probe */ + inb_mc_attach, /* devo_attach */ + inb_mc_detach, /* devo_detach */ + nodev, /* devo_reset */ + &inb_mc_cb_ops, /* devo_cb_ops */ + NULL, /* devo_bus_ops */ + NULL /* devo_power */ +}; + +static struct modldrv modldrv = { + &mod_driverops, + "Intel 5000 Memory Controller Hub Module", + &inb_mc_ops +}; + +static struct modlinkage modlinkage = { + MODREV_1, + (void *)&modldrv, + NULL +}; + +int +_init(void) +{ + int err; + + err = nb_init(); + if (err == 0 && (err = mod_install(&modlinkage)) == 0) + rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL); + + return (err); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int err; + + if ((err = mod_remove(&modlinkage)) == 0) { + nb_unload(); + rw_destroy(&inb_mc_lock); + } + + return (err); +} diff --git a/usr/src/uts/i86pc/io/intel_nb5000/nb5000.h b/usr/src/uts/i86pc/io/intel_nb5000/nb5000.h new file mode 100644 index 0000000000..7ee8f2dabf --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/nb5000.h @@ -0,0 +1,802 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NB5000_H +#define _NB5000_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/cpu_module.h> + +#define NB_5000_MAX_MEM_CONTROLLERS 2 +#define NB_MEM_BRANCH_SELECT 3 +#define NB_MEM_RANK_SELECT 5 +#define NB_RANKS_IN_SELECT 4 +#define NB_PCI_DEV 8 + +#define NB_PCI_NFUNC 4 + +#define DOCMD_PEX_MASK 0xc0 +#define DOCMD_PEX 0x3f + +#define SPD_BUSY 0x1000 +#define SPD_BUS_ERROR 0x2000 +#define SPD_READ_DATA_VALID 0x8000 +#define SPD_EEPROM_WRITE 0xa8000000 +#define SPD_ADDR(slave, addr) ((((slave) & 7) << 24) | (((addr) & 0xff) << 16)) + +#define MC_MIRROR 0x10000 +#define MC_PATROL_SCRUB 0x80 +#define MC_DEMAND_SCRUB 0x40 + +#define MCA_SCHDIMM 0x4000 + +#define TLOW_MAX 0x100000000ULL + +#define MTR_PRESENT(mtr) ((mtr) & 0x10) +#define MTR_ETHROTTLE(mtr) ((mtr) & 8) +#define MTR_WIDTH(mtr) (((((mtr) >> 6) & 1) + 1) * 4) +#define MTR_NUMBANK(mtr) (((((mtr) >> 5) & 1) + 1) * 4) +#define MTR_NUMRANK(mtr) ((((mtr) >> 4) & 1) + 1) +#define MTR_NUMROW(mtr) ((((mtr) >> 2) & 3) + 13) +#define MTR_NUMCOL(mtr) (((mtr) & 3) + 10) +#define MTR_DIMMSIZE(mtr) ((1 << (MTR_NUMCOL(mtr) + MTR_NUMROW(mtr))) \ + * MTR_NUMRANK(mtr) * MTR_NUMBANK(mtr) * MTR_WIDTH(mtr)) + +/* FERR_GLOBAL and NERR_GLOBAL */ +#define GE_FERR_FSB3_FATAL 0x800000000ULL /* FSB3 Fatal Error */ +#define GE_FERR_FSB2_FATAL 0x400000000ULL /* FSB2 Fatal Error */ +#define GE_FERR_FSB3_NF 0x200000000ULL /* FSB3 Non-Fatal Error */ +#define GE_FERR_FSB2_NF 0x100000000ULL /* FSB2 Non-Fatal Error */ + +#define GE_INT_FATAL 0x80000000 /* North Bridge Internal Error */ +#define GE_DMA_FATAL 0x40000000 /* DMA engine Fatal Error */ +#define GE_FSB1_FATAL 0x20000000 /* FSB1 Fatal Error */ +#define GE_FSB0_FATAL 0x10000000 /* FSB0 Fatal Error */ +#define GE_FERR_FBD3_FATAL 0x08000000 /* FBD3 channel Fatal Error */ +#define GE_FERR_FBD2_FATAL 0x04000000 /* FBD2 channel Fatal Error */ +#define GE_FERR_FBD1_FATAL 0x02000000 /* FBD1 channel Fatal Error */ +#define GE_FERR_FBD0_FATAL 0x01000000 /* FBD0 channel Fatal Error */ +#define GE_PCIEX7_FATAL 0x00800000 /* PCI Express device 7 Fatal Error */ +#define GE_PCIEX6_FATAL 0x00400000 /* PCI Express device 6 Fatal Error */ +#define GE_PCIEX5_FATAL 0x00200000 /* PCI Express device 5 Fatal Error */ +#define GE_PCIEX4_FATAL 0x00100000 /* PCI Express device 4 Fatal Error */ +#define GE_PCIEX3_FATAL 0x00080000 /* PCI Express device 3 Fatal Error */ +#define GE_PCIEX2_FATAL 0x00040000 /* PCI Express device 2 Fatal Error */ +#define GE_PCIEX1_FATAL 0x00020000 /* PCI Express device 1 Fatal Error */ +#define GE_ESI_FATAL 0x00010000 /* ESI Fatal Error */ +#define GE_INT_NF 0x00008000 /* North Bridge Internal Error */ +#define GE_DMA_NF 0x00004000 /* DMA engine Non-Fatal Error */ +#define GE_FSB1_NF 0x00002000 /* FSB1 Non-Fatal Error */ +#define GE_FSB0_NF 0x00001000 /* FSB0 Non-Fatal Error */ +#define GE_FERR_FBD3_NF 0x00000800 /* FBD channel 3 Non-Fatal Error */ +#define GE_FERR_FBD2_NF 0x00000400 /* FBD channel 2 Non-Fatal Error */ +#define GE_FERR_FBD1_NF 0x00000200 /* FBD channel 1 Non-Fatal Error */ +#define GE_FERR_FBD0_NF 0x00000100 /* FBD channel 0 Non-Fatal Error */ +#define GE_PCIEX7_NF 0x00000080 /* PCI Express dev 7 Non-Fatal Error */ +#define GE_PCIEX6_NF 0x00000040 /* PCI Express dev 6 Non-Fatal Error */ +#define GE_PCIEX5_NF 0x00000020 /* PCI Express dev 5 Non-Fatal Error */ +#define GE_PCIEX4_NF 0x00000010 /* PCI Express dev 4 Non-Fatal Error */ +#define GE_PCIEX3_NF 0x00000008 /* PCI Express dev 3 Non-Fatal Error */ +#define GE_PCIEX2_NF 0x00000004 /* PCI Express dev 2 Non-Fatal Error */ +#define GE_PCIEX1_NF 0x00000002 /* PCI Express dev 1 Non-Fatal Error */ +#define GE_ESI_NF 0x00000001 /* ESI Non-Fatal Error */ + +#define GE_NERR_FSB2_FATAL 0x08000000 /* FSB2 Fatal Error */ +#define GE_NERR_FSB3_FATAL 0x04000000 /* FSB3 Fatal Error */ +#define GE_NERR_FBD_FATAL 0x01000000 /* FBD channel Fatal Error */ +#define GE_NERR_FSB2_NF 0x00000800 /* FSB2 Non-Fatal Error */ +#define GE_NERR_FSB3_NF 0x00000400 /* FSB3 Non-Fatal Error */ +#define GE_NERR_FBD_NF 0x00000100 /* FBD channel Non-Fatal Error */ + +#define ERR_FAT_FSB_F9 0x20 /* F9Msk FSB Protocol */ +#define ERR_FAT_FSB_F2 0x08 /* F2Msk Unsupported Bus Transaction */ +#define ERR_FAT_FSB_F1 0x01 /* F1Msk Request/Address Parity */ + +#define ERR_NF_FSB_F7 0x04 /* F7Msk Detected MCERR */ +#define ERR_NF_FSB_F8 0x02 /* F8Msk B-INIT */ +#define ERR_NF_FSB_F6 0x01 /* F6Msk Data Parity */ + +#define EMASK_FSB_F1 0x0001 /* F1Msk Request/Address Parity */ +#define EMASK_FSB_F2 0x0002 /* F2Msk Unsupported Bus Transaction */ +#define EMASK_FSB_F6 0x0020 /* F6Msk Data Parity */ +#define EMASK_FSB_F7 0x0040 /* F7Msk Detected MCERR */ +#define EMASK_FSB_F8 0x0080 /* F8Msk B-INIT */ +#define EMASK_FSB_F9 0x0100 /* F9Msk FSB Protocol */ + +#define EMASK_FSB_FATAL (EMASK_FSB_F1 | EMASK_FSB_F2 | EMASK_FSB_F9) +#define EMASK_FSB_NF (EMASK_FSB_F6 | EMASK_FSB_F7 | EMASK_FSB_F8) + +#define ERR_FBD_CH_SHIFT 28 /* channel index in fat_fbd and nf_fbd */ + +#define ERR_FAT_FBD_M23 0x00400000 /* M23Err Non-Redundant Fast Reset */ + /* Timeout */ +#define ERR_FAT_FBD_M3 0x00000004 /* M3Err >Tmid thermal event with */ + /* intelligent throttling disabled */ +#define ERR_FAT_FBD_M2 0x00000002 /* M2Err memory or FBD configuration */ + /* CRC read error */ +#define ERR_FAT_FBD_M1 0x00000001 /* M1Err memory write error on */ + /* non-redundant retry or FBD */ + /* configuration write error on retry */ +#define ERR_FAT_FBD_MASK 0x007fffff + +#define ERR_NF_FBD_M28 0x01000000 /* M28Err DIMM-Spare Copy Completed */ +#define ERR_NF_FBD_M27 0x00800000 /* M27Err DIMM-Spare Copy Initiated */ +#define ERR_NF_FBD_M26 0x00400000 /* M26Err Redundant Fast Reset */ + /* Timeout */ +#define ERR_NF_FBD_M25 0x00200000 /* M25Err Memory write error on */ + /* redundant retry */ +#define ERR_NF_FBD_M22 0x00040000 /* M22Err SPD protocol */ +#define ERR_NF_FBD_M21 0x00020000 /* M21Err FBD Northbound parity on */ + /* FBD sync status */ +#define ERR_NF_FBD_M20 0x00010000 /* M20Err Correctable patrol data ECC */ +#define ERR_NF_FBD_M19 0x00008000 /* M19Err Correctasble resilver or */ + /* spare-copy data ECC */ +#define ERR_NF_FBD_M18 0x00004000 /* M18Err Correctable Mirrored demand */ + /* data ECC */ +#define ERR_NF_FBD_M17 0x00002000 /* M17Err Correctable Non-mirrored */ + /* demand data ECC */ +#define ERR_NF_FBD_M15 0x00000800 /* M15Err Memory or FBD configuration */ + /* CRC read error */ +#define ERR_NF_FBD_M14 0x00000400 /* M14Err FBD configuration write */ + /* error on first attempt */ +#define ERR_NF_FBD_M13 0x00000200 /* M13Err Memory write error on first */ + /* attempt */ +#define ERR_NF_FBD_M12 0x00000100 /* M12Err Non-Aliased uncorrectable */ + /* patrol data ECC */ +#define ERR_NF_FBD_M11 0x00000080 /* M11Err Non-Aliased uncorrectable */ + /* resilver or spare copy data ECC */ +#define ERR_NF_FBD_M10 0x00000040 /* M10Err Non-Aliased uncorrectable */ + /* mirrored demand data ECC */ +#define ERR_NF_FBD_M9 0x00000020 /* M9Err Non-Aliased uncorrectable */ + /* non-mirrored demand data ECC */ +#define ERR_NF_FBD_M8 0x00000010 /* M8Err Aliased uncorrectable */ + /* patrol data ECC */ +#define ERR_NF_FBD_M7 0x00000008 /* M7Err Aliased uncorrectable */ + /* resilver or spare copy data ECC */ +#define ERR_NF_FBD_M6 0x00000004 /* M6Err Aliased uncorrectable */ + /* mirrored demand data ECC */ +#define ERR_NF_FBD_M5 0x00000002 /* M5Err Aliased uncorrectable */ + /* non-mirrored demand data ECC */ +#define ERR_NF_FBD_M4 0x00000001 /* M4Err uncorrectable data ECC on */ + /* replay */ + +#define ERR_NF_FBD_MASK 0x01ffffff +#define ERR_NF_FBD_ECC_UE (ERR_NF_FBD_M12|ERR_NF_FBD_M11|ERR_NF_FBD_M10| \ + ERR_NF_FBD_M9|ERR_NF_FBD_M8|ERR_NF_FBD_M7|ERR_NF_FBD_M6|ERR_NF_FBD_M5| \ + ERR_NF_FBD_M4) +#define ERR_NF_FBD_MA (ERR_NF_FBD_M14) +#define ERR_NF_FBD_ECC_CE (ERR_NF_FBD_M20|ERR_NF_FBD_M19|ERR_NF_FBD_M18| \ + ERR_NF_FBD_M17|ERR_NF_FBD_M15|ERR_NF_FBD_M21) +#define ERR_NF_FBD_SPARE (ERR_NF_FBD_M28|ERR_NF_FBD_M27) + +#define EMASK_FBD_M28 0x08000000 /* M28Err DIMM-Spare Copy Completed */ +#define EMASK_FBD_M27 0x04000000 /* M27Err DIMM-Spare Copy Initiated */ +#define EMASK_FBD_M26 0x02000000 /* M26Err Redundant Fast Reset */ + /* Timeout */ +#define EMASK_FBD_M25 0x01000000 /* M25Err Memory write error on */ + /* redundant retry */ +#define EMASK_FBD_M23 0x00400000 /* M23Err Non-Redundant Fast Reset */ + /* Timeout */ +#define EMASK_FBD_M22 0x00200000 /* M22Err SPD protocol */ +#define EMASK_FBD_M21 0x00100000 /* M21Err FBD Northbound parity on */ + /* FBD sync status */ +#define EMASK_FBD_M20 0x00080000 /* M20Err Correctable patrol data ECC */ +#define EMASK_FBD_M19 0x00040000 /* M19Err Correctasble resilver or */ + /* spare-copy data ECC */ +#define EMASK_FBD_M18 0x00020000 /* M18Err Correctable Mirrored demand */ + /* data ECC */ +#define EMASK_FBD_M17 0x00010000 /* M17Err Correctable Non-mirrored */ + /* demand data ECC */ +#define EMASK_FBD_M15 0x00004000 /* M15Err Memory or FBD configuration */ + /* CRC read error */ +#define EMASK_FBD_M14 0x00002000 /* M14Err FBD configuration write */ + /* error on first attempt */ +#define EMASK_FBD_M13 0x00001000 /* M13Err Memory write error on first */ + /* attempt */ +#define EMASK_FBD_M12 0x00000800 /* M12Err Non-Aliased uncorrectable */ + /* patrol data ECC */ +#define EMASK_FBD_M11 0x00000400 /* M11Err Non-Aliased uncorrectable */ + /* resilver or spare copy data ECC */ +#define EMASK_FBD_M10 0x00000200 /* M10Err Non-Aliased uncorrectable */ + /* mirrored demand data ECC */ +#define EMASK_FBD_M9 0x00000100 /* M9Err Non-Aliased uncorrectable */ + /* non-mirrored demand data ECC */ +#define EMASK_FBD_M8 0x00000080 /* M8Err Aliased uncorrectable */ + /* patrol data ECC */ +#define EMASK_FBD_M7 0x00000040 /* M7Err Aliased uncorrectable */ + /* resilver or spare copy data ECC */ +#define EMASK_FBD_M6 0x00000020 /* M6Err Aliased uncorrectable */ + /* mirrored demand data ECC */ +#define EMASK_FBD_M5 0x00000010 /* M5Err Aliased uncorrectable */ + /* non-mirrored demand data ECC */ +#define EMASK_FBD_M4 0x00000008 /* M4Err uncorrectable data ECC on */ + /* replay */ +#define EMASK_FBD_M3 0x00000004 /* M3Err >Tmid thermal event with */ + /* intelligent throttling disabled */ +#define EMASK_FBD_M2 0x00000002 /* M2Err memory or FBD configuration */ + /* CRC read error */ +#define EMASK_FBD_M1 0x00000001 /* M1Err memory write error on */ + /* non-redundant retry or FBD */ + /* configuration write error on retry */ +/* MCH 7300 errata 34 */ +#define EMASK_FBD_RES 0x00808000 /* reserved mask bits */ + +#define EMASK_FBD_FATAL (EMASK_FBD_M23|EMASK_FBD_M3|EMASK_FBD_M2|EMASK_FBD_M1) +#define EMASK_FBD_NF (EMASK_FBD_M28|EMASK_FBD_M27|EMASK_FBD_M26|EMASK_FBD_M25| \ + EMASK_FBD_M22|EMASK_FBD_M21|EMASK_FBD_M20|EMASK_FBD_M19|EMASK_FBD_M18| \ + EMASK_FBD_M17|EMASK_FBD_M15|EMASK_FBD_M14|EMASK_FBD_M13|EMASK_FBD_M12| \ + EMASK_FBD_M11|EMASK_FBD_M10|EMASK_FBD_M9|EMASK_FBD_M8|EMASK_FBD_M7| \ + EMASK_FBD_M6|EMASK_FBD_M5|EMASK_FBD_M4) + +#define ERR_FAT_INT_B8 0x10 /* B8Msk SF Coherency Error for BIL */ +#define ERR_FAT_INT_B4 0x08 /* B4Msk Virtual pin port error */ +#define ERR_FAT_INT_B3 0x04 /* B3Msk Coherency violation error for EWB */ +#define ERR_FAT_INT_B2 0x02 /* B2Msk Multi-tag hit SF */ +#define ERR_FAT_INT_B1 0x01 /* B1Msk DM parity error */ + +#define ERR_NF_INT_B7 0x04 /* B7Msk Multiple ECC error in any of */ + /* the ways during SF lookup */ +#define ERR_NF_INT_B6 0x02 /* B6Msk Single ECC error on SF lookup */ +#define ERR_NF_INT_B5 0x01 /* B5Msk Address Map error */ + +#define EMASK_INT_B8 0x80 /* B8Msk SF Coherency Error for BIL */ +#define EMASK_INT_B7 0x40 /* B7Msk Multiple ECC error in any of */ + /* the ways during SF lookup */ +#define EMASK_INT_B6 0x20 /* B6Msk Single ECC error on SF lookup */ +#define EMASK_INT_B5 0x10 /* B5Msk Address Map error */ +#define EMASK_INT_B4 0x08 /* B4Msk Virtual pin port error */ +#define EMASK_INT_B3 0x04 /* B3Msk Coherency violation error for EWB */ +#define EMASK_INT_B2 0x02 /* B2Msk Multi-tag hit SF */ +#define EMASK_INT_B1 0x01 /* B1Msk DM parity error */ + +/* MCH 5000 errata 2 */ +#define EMASK_INT_5000 EMASK_INT_B1 +/* MCH 7300 errata 17 & 20 */ +#define EMASK_INT_7300 (EMASK_INT_B3|EMASK_INT_B1) +/* MCH 7300 errata 17,20 & 21 */ +#define EMASK_INT_7300_STEP_0 (EMASK_INT_B7|EMASK_INT_B3|EMASK_INT_B1) + +#define EMASK_INT_FATAL (EMASK_INT_B7|EMASK_INT_B4|EMASK_INT_B3|EMASK_INT_B2| \ + EMASK_INT_B1) +#define EMASK_INT_NF (EMASK_INT_B8|EMASK_INT_B6|EMASK_INT_B5) +#define GE_FBD_FATAL (GE_FERR_FBD0_FATAL|GE_FERR_FBD1_FATAL| \ + GE_FERR_FBD2_FATAL|GE_FERR_FBD3_FATAL) +#define GE_FBD_NF \ + (GE_FERR_FBD0_NF|GE_FERR_FBD1_NF|GE_FERR_FBD2_NF|GE_FERR_FBD3_NF) + +#define EMASK_UNCOR_PEX_IO18 0x00200000 /* ESI Reset timeout */ +#define EMASK_UNCOR_PEX_IO2 0x00100000 /* Received an unsupported */ + /* request */ +#define EMASK_UNCOR_PEX_IO9 0x00040000 /* Malformed TLP Status */ +#define EMASK_UNCOR_PEX_IO10 0x00020000 /* Received buffer overflow */ +#define EMASK_UNCOR_PEX_IO8 0x00010000 /* unexpected completion */ +#define EMASK_UNCOR_PEX_IO7 0x00008000 /* completion abort */ +#define EMASK_UNCOR_PEX_IO6 0x00004000 /* completion timeout */ +#define EMASK_UNCOR_PEX_IO5 0x00002000 /* flow control protocol */ +#define EMASK_UNCOR_PEX_IO4 0x00001000 /* poisoned TLP */ +#define EMASK_UNCOR_PEX_IO19 0x00000020 /* surprise link down */ +#define EMASK_UNCOR_PEX_IO0 0x00000010 /* data link protocol */ +#define EMASK_UNCOR_PEX_IO3 0x00000001 /* training error */ + +#define EMASK_COR_PEX_NF_EDM 0x00002000 /* Advisory Non Fatal */ +#define EMASK_COR_PEX_IO16 0x00001000 /* replay timer timeout */ +#define EMASK_COR_PEX_IO15 0x00000100 /* replay num pollover */ +#define EMASK_COR_PEX_IO14 0x00000080 /* bad DLLP */ +#define EMASK_COR_PEX_IO13 0x00000040 /* bad TLP */ +#define EMASK_COR_PEX_IO12 0x00000001 /* receiver error mask */ + +#define EMASK_RP_PEX_IO1 0x00000004 /* fatal message detect */ +#define EMASK_RP_PEX_IO11 0x00000002 /* uncorrectable message */ +#define EMASK_RP_PEX_IO17 0x00000001 /* correctable message */ + +#define PEX_FAT_IO19 0x00001000 /* surprise link down */ +#define PEX_FAT_IO18 0x00000800 /* ESI reset timeout */ +#define PEX_FAT_IO9 0x00000400 /* malformed TLP */ +#define PEX_FAT_IO10 0x00000200 /* receiver buffer overflow */ +#define PEX_FAT_IO8 0x00000100 /* unexpected completion */ +#define PEX_FAT_IO7 0x00000080 /* completer abort */ +#define PEX_FAT_IO6 0x00000040 /* completion timeout */ +#define PEX_FAT_IO5 0x00000020 /* flow control protocol */ +#define PEX_FAT_IO4 0x00000010 /* poisoned TLP */ +#define PEX_FAT_IO3 0x00000008 /* training error */ +#define PEX_FAT_IO2 0x00000004 /* received unsupported req */ +#define PEX_FAT_IO1 0x00000002 /* received fatal error message */ +#define PEX_FAT_IO0 0x00000001 /* data link layer protocol */ + +#define PEX_NF_IO19 0x00020000 /* surprise link down */ +#define PEX_NF_IO17 0x00010000 /* received correctable error message */ +#define PEX_NF_IO16 0x00008000 /* replay timer timeout */ +#define PEX_NF_IO15 0x00004000 /* replay num pollover */ +#define PEX_NF_IO14 0x00002000 /* bad DLLP */ +#define PEX_NF_IO13 0x00001000 /* bad TLP */ +#define PEX_NF_IO12 0x00000800 /* receiver error mask */ +#define PEX_NF_IO11 0x00000400 /* received non fatal error message */ +#define PEX_NF_IO10 0x00000200 /* Received buffer overflow */ +#define PEX_NF_IO9 0x00000100 /* Malformed TLP */ +#define PEX_NF_IO8 0x00000080 +#define PEX_NF_IO7 0x00000040 +#define PEX_NF_IO6 0x00000020 /* completion timeout */ +#define PEX_NF_IO5 0x00000010 /* flow control protocol */ +#define PEX_NF_IO4 0x00000008 /* poisoned TLP */ +#define PEX_NF_IO3 0x00000004 +#define PEX_NF_IO2 0x00000002 +#define PEX_NF_IO0 0x00000001 /* data link layer protocol */ + +#define GE_FERR_FSB(ferr) ( \ + ((ferr) & (GE_FSB0_FATAL|GE_FSB0_NF)) ? 0 : \ + ((ferr) & (GE_FSB1_FATAL|GE_FSB1_NF)) ? 1 : \ + (nb_chipset == INTEL_NB_7300) && \ + ((ferr) & (GE_FERR_FSB2_FATAL|GE_FERR_FSB2_NF)) ? 2 : \ + (nb_chipset == INTEL_NB_7300) && \ + ((ferr) & (GE_FERR_FSB3_FATAL|GE_FERR_FSB3_NF)) ? 3 : \ + -1) + +#define GE_NERR_TO_FERR_FSB(nerr) \ + (((nerr) & GE_NERR_FSB3_FATAL) ? GE_FERR_FSB3_FATAL : 0 | \ + ((nerr) & GE_NERR_FSB2_FATAL) ? GE_FERR_FSB2_FATAL : 0 | \ + ((nerr) & GE_NERR_FSB3_NF) ? GE_FERR_FBD3_NF : 0 | \ + ((nerr) & GE_NERR_FSB2_NF) ? GE_FERR_FBD2_NF : 0) + +#define GE_ERR_PEX(ferr) ( \ + ((ferr) & (GE_ESI_FATAL|GE_ESI_NF)) ? 0 : \ + ((nb_chipset == INTEL_NB_7300) && \ + ((ferr) & (GE_PCIEX1_FATAL|GE_PCIEX1_NF))) ? 1 : \ + ((ferr) & (GE_PCIEX2_FATAL|GE_PCIEX2_NF)) ? 2 : \ + ((ferr) & (GE_PCIEX3_FATAL|GE_PCIEX3_NF)) ? 3 : \ + ((ferr) & (GE_PCIEX4_FATAL|GE_PCIEX4_NF)) ? 4 : \ + ((ferr) & (GE_PCIEX5_FATAL|GE_PCIEX5_NF)) ? 5 : \ + ((ferr) & (GE_PCIEX6_FATAL|GE_PCIEX6_NF)) ? 6 : \ + ((ferr) & (GE_PCIEX7_FATAL|GE_PCIEX7_NF)) ? 7 : \ + -1) + +#define GE_FERR_FATAL ((nb_chipset == INTEL_NB_7300) ? \ + (GE_INT_FATAL|GE_DMA_FATAL|GE_FERR_FSB3_FATAL|GE_FERR_FSB2_FATAL| \ + GE_FSB1_FATAL|GE_FSB0_FATAL|GE_FBD_FATAL|GE_PCIEX7_FATAL| \ + GE_PCIEX6_FATAL| GE_PCIEX5_FATAL|GE_PCIEX4_FATAL|GE_PCIEX3_FATAL| \ + GE_PCIEX2_FATAL| GE_ESI_FATAL) : \ + (GE_INT_FATAL|GE_DMA_FATAL|GE_FSB1_FATAL|GE_FSB0_FATAL|GE_FBD_FATAL| \ + GE_PCIEX7_FATAL|GE_PCIEX6_FATAL|GE_PCIEX5_FATAL|GE_PCIEX4_FATAL| \ + GE_PCIEX3_FATAL|GE_PCIEX2_FATAL|GE_ESI_FATAL)) + +#define GE_NERR_FATAL ((nb_chipset == INTEL_NB_7300) ? \ + (GE_INT_FATAL|GE_DMA_FATAL|GE_NERR_FSB3_FATAL|GE_NERR_FSB2_FATAL| \ + GE_FSB1_FATAL|GE_FSB0_FATAL|GE_FBD_FATAL|GE_PCIEX7_FATAL| \ + GE_PCIEX6_FATAL|GE_PCIEX5_FATAL|GE_PCIEX4_FATAL|GE_PCIEX3_FATAL| \ + GE_PCIEX2_FATALGE_ESI_FATAL) : \ + (GE_INT_FATAL|GE_DMA_FATAL|GE_FSB1_FATAL|GE_FSB0_FATAL|GE_FBD_FATAL| \ + GE_PCIEX7_FATAL|GE_PCIEX6_FATAL|GE_PCIEX5_FATAL|GE_PCIEX4_FATAL| \ + GE_PCIEX3_FATAL|GE_PCIEX2_FATAL|GE_ESI_FATAL)) + +#define GE_PCIEX_FATAL (GE_ESI_FATAL|GE_PCIEX1_FATAL|GE_PCIEX2_FATAL| \ + GE_PCIEX3_FATAL|GE_PCIEX4_FATAL|GE_PCIEX5_FATAL|GE_PCIEX6_FATAL| \ + GE_PCIEX7_FATAL) +#define GE_PCIEX_NF (GE_ESI_NF|GE_PCIEX1_NF|GE_PCIEX2_NF|GE_PCIEX3_NF| \ + GE_PCIEX4_NF|GE_PCIEX5_NF|GE_PCIEX6_NF|GE_PCIEX7_NF) +#define GE_FERR_FSB_FATAL ((nb_chipset == INTEL_NB_7300) ? \ + (GE_FSB0_FATAL|GE_FSB1_FATAL|GE_FERR_FSB2_FATAL|GE_FERR_FSB3_FATAL) : \ + (GE_FSB0_FATAL|GE_FSB1_FATAL)) +#define GE_NERR_FSB_FATAL ((nb_chipset == INTEL_NB_7300) ? \ + (GE_FSB0_FATAL|GE_FSB1_FATAL|GE_NERR_FSB2_FATAL|GE_NERR_FSB3_FATAL) : \ + (GE_FSB0_FATAL|GE_FSB1_FATAL)) +#define GE_FERR_FSB_NF ((nb_chipset == INTEL_NB_7300) ? \ + (GE_FSB0_NF|GE_FSB1_NF|GE_FERR_FSB2_NF|GE_FERR_FSB3_NF) : \ + (GE_FSB0_NF|GE_FSB1_NF|GE_FERR_FSB2_NF|GE_FERR_FSB3_NF)) +#define GE_NERR_FSB_NF ((nb_chipset == INTEL_NB_7300) ? \ + (GE_FSB0_NF|GE_FSB1_NF|GE_NERR_FSB2_NF|GE_NERR_FSB3_NF) : \ + (GE_FSB0_NF|GE_FSB1_NF|GE_NERR_FSB2_NF|GE_NERR_FSB3_NF)) + +#define FERR_FBD_CHANNEL(reg) ((reg)>>28 & 3) + +#define NB5000_STEPPING() nb_pci_getw(0, 0, 0, 8, 0) + +#define FERR_GLOBAL_RD() ((nb_chipset == INTEL_NB_7300) ? \ + ((uint64_t)nb_pci_getl(0, 16, 2, \ + 0x48, 0) << 32) | nb_pci_getl(0, 16, 2, \ + 0x40, 0) : \ + (uint64_t)nb_pci_getl(0, 16, 2, 0x40, 0)) +#define NERR_GLOBAL_RD() nb_pci_getl(0, 16, 2, 0x44, 0) +#define FERR_FAT_FSB_RD(fsb, ip) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc0 : 0x40, ip) : \ + nb_pci_getb(0, 16, 0, fsb ? 0x480 : 0x180, ip)) +#define FERR_NF_FSB_RD(fsb, ip) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc1 : 0x41, ip) : \ + nb_pci_getb(0, 16, 0, fsb ? 0x481 : 0x181, ip)) +#define NERR_FAT_FSB_RD(fsb, ip) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc2 : 0x42, ip) : \ + nb_pci_getb(0, 16, 0, fsb ? 0x482 : 0x182, ip)) +#define NERR_NF_FSB_RD(fsb, ip) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc3 : 0x43, ip) : \ + nb_pci_getb(0, 16, 0, fsb ? 0x483 : 0x183, ip)) + +#define NRECFSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc4 : 0x44, 0) : \ + nb_pci_getl(0, 16, 0, fsb ? 0x484 : 0x184, 0)) +#define NRECFSB_WR(fsb) \ + if (nb_chipset == INTEL_NB_7300) { \ + nb_pci_putl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc4 : 0x44, \ + 0); \ + } else { \ + nb_pci_putl(0, 16, 0, fsb ? 0x484 : 0x184, 0); \ + } +#define RECFSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc8 : 0x48, 0) : \ + nb_pci_getl(0, 16, 0, fsb ? 0x488 : 0x188, 0)) +#define RECFSB_WR(fsb) \ + if (nb_chipset == INTEL_NB_7300) { \ + nb_pci_putl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc8 : 0x48, \ + 0); \ + } else { \ + nb_pci_putl(0, 16, 0, fsb ? 0x488 : 0x188, 0); \ + } +#define NRECADDR_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + ((uint64_t)(nb_pci_getb(0, 17, (fsb & 2) ? 3 : 0, \ + (fsb & 1) ? 0xd0 : 0x50, 0)) << 32) | \ + nb_pci_getl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xcc : 0x4c, 0) : \ + ((uint64_t)(nb_pci_getb(0, 16, 0, fsb ? 0x490 : 0x190, 0)) << 32) | \ + nb_pci_getl(0, 16, 0, fsb ? 0x48c : 0x18c, 0)) +#define NRECADDR_WR(fsb) \ + if (nb_chipset == INTEL_NB_7300) { \ + nb_pci_putb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd0 : 0x50, \ + 0); \ + nb_pci_putl(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xcc : 0x4c, \ + 0); \ + } else { \ + nb_pci_putb(0, 16, 0, fsb ? 0x490 : 0x190, 0); \ + nb_pci_putl(0, 16, 0, fsb ? 0x48c : 0x18c, 0); \ + } +#define EMASK_FSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd2 : 0x52, 0) : \ + nb_pci_getw(0, 16, 0, fsb ? 0x492 : 0x192, 0)) +#define ERR0_FSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd4 : 0x54, 0) : \ + nb_pci_getw(0, 16, 0, fsb ? 0x494 : 0x194, 0)) +#define ERR1_FSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd6 : 0x56, 0) : \ + nb_pci_getw(0, 16, 0, fsb ? 0x496 : 0x196, 0)) +#define ERR2_FSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd8 : 0x58, 0) : \ + nb_pci_getw(0, 16, 0, fsb ? 0x498 : 0x198, 0)) +#define MCERR_FSB_RD(fsb) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_getw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xda : 0x5a, 0) : \ + nb_pci_getw(0, 16, 0, fsb ? 0x49a : 0x19a, 0)) + +#define FERR_GLOBAL_WR(val) \ + if (nb_chipset == INTEL_NB_7300) \ + { \ + nb_pci_putl(0, 16, 2, 0x48, (uint32_t)(val >> 32)); \ + nb_pci_putl(0, 16, 2, 0x40, (uint32_t)val); \ + } else { \ + nb_pci_putl(0, 16, 2, 0x40, (uint32_t)val); \ + } +#define NERR_GLOBAL_WR(val) nb_pci_putl(0, 16, 2, 0x44, val) +#define FERR_FAT_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc0 : 0x40, val) : \ + nb_pci_putb(0, 16, 0, fsb ? 0x480 : 0x180, val)) +#define FERR_NF_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc1 : 0x41, val) : \ + nb_pci_putb(0, 16, 0, fsb ? 0x481 : 0x181, val)) +#define NERR_FAT_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc2 : 0x42, val) : \ + nb_pci_putb(0, 16, 0, fsb ? 0x482 : 0x182, val)) +#define NERR_NF_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putb(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xc3 : 0x43, val) : \ + nb_pci_putb(0, 16, 0, fsb ? 0x483 : 0x183, val)) +#define EMASK_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd2 : 0x52, val) : \ + nb_pci_putw(0, 16, 0, fsb ? 0x492 : 0x192, val)) +#define ERR0_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd4 : 0x54, val) : \ + nb_pci_putw(0, 16, 0, fsb ? 0x494 : 0x194, val)) +#define ERR1_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd6 : 0x56, val) : \ + nb_pci_putw(0, 16, 0, fsb ? 0x496 : 0x196, val)) +#define ERR2_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xd8 : 0x58, val) : \ + nb_pci_putw(0, 16, 0, fsb ? 0x498 : 0x198, val)) +#define MCERR_FSB_WR(fsb, val) ((nb_chipset == INTEL_NB_7300) ? \ + nb_pci_putw(0, 17, (fsb & 2) ? 3 : 0, (fsb & 1) ? 0xda : 0x5a, val) : \ + nb_pci_putw(0, 16, 0, fsb ? 0x49a : 0x19a, val)) + +#define NRECSF_RD() (nb_chipset == INTEL_NB_5000X || \ + nb_chipset == INTEL_NB_7300) ? ((uint64_t)( \ + nb_pci_getl(0, 16, 2, 0xb4, 0)) << 32) | \ + nb_pci_getl(0, 16, 2, 0xb0, 0) : 0LL +#define RECSF_RD() (nb_chipset == INTEL_NB_5000X || \ + nb_chipset == INTEL_NB_7300) ? ((uint64_t)( \ + nb_pci_getl(0, 16, 2, 0xbc, 0)) << 32) | \ + nb_pci_getl(0, 16, 2, 0xb8, 0) : 0LL + +#define NRECSF_WR() if (nb_chipset == INTEL_NB_5000X || \ + nb_chipset == INTEL_NB_7300) { \ + nb_pci_putl(0, 16, 2, 0xbc, 0); \ + nb_pci_putl(0, 16, 2, 0xb0, 0); \ + } +#define RECSF_WR() if (nb_chipset == INTEL_NB_5000X || \ + nb_chipset == INTEL_NB_7300) { \ + nb_pci_putl(0, 16, 2, 0xbc, 0); \ + nb_pci_putl(0, 16, 2, 0xb8, 0); \ + } + +#define FERR_FAT_INT_RD(ip) nb_pci_getb(0, 16, 2, 0xc0, ip) +#define FERR_NF_INT_RD(ip) nb_pci_getb(0, 16, 2, 0xc1, ip) +#define NERR_FAT_INT_RD(ip) nb_pci_getb(0, 16, 2, 0xc2, ip) +#define NERR_NF_INT_RD(ip) nb_pci_getb(0, 16, 2, 0xc3, ip) +#define EMASK_INT_RD() nb_pci_getb(0, 16, 2, 0xcc, 0) +#define ERR0_INT_RD() nb_pci_getb(0, 16, 2, 0xd0, 0) +#define ERR1_INT_RD() nb_pci_getb(0, 16, 2, 0xd1, 0) +#define ERR2_INT_RD() nb_pci_getb(0, 16, 2, 0xd2, 0) +#define MCERR_INT_RD() nb_pci_getb(0, 16, 2, 0xd3, 0) + +#define FERR_FAT_INT_WR(val) nb_pci_putb(0, 16, 2, 0xc0, val) +#define FERR_NF_INT_WR(val) nb_pci_putb(0, 16, 2, 0xc1, val) +#define NERR_FAT_INT_WR(val) nb_pci_putb(0, 16, 2, 0xc2, val) +#define NERR_NF_INT_WR(val) nb_pci_putb(0, 16, 2, 0xc3, val) +#define EMASK_INT_WR(val) nb_pci_putb(0, 16, 2, 0xcc, val) +#define ERR0_INT_WR(val) nb_pci_putb(0, 16, 2, 0xd0, val) +#define ERR1_INT_WR(val) nb_pci_putb(0, 16, 2, 0xd1, val) +#define ERR2_INT_WR(val) nb_pci_putb(0, 16, 2, 0xd2, val) +#define MCERR_INT_WR(val) nb_pci_putb(0, 16, 2, 0xd3, val) + +#define NRECINT_RD() nb_pci_getl(0, 16, 2, 0xc4, 0) +#define RECINT_RD() nb_pci_getl(0, 16, 2, 0xc8, 0) + +#define NRECINT_WR() nb_pci_putl(0, 16, 2, 0xc4, 0) +#define RECINT_WR() nb_pci_putl(0, 16, 2, 0xc8, 0) + +#define FERR_FAT_FBD_RD(ip) nb_pci_getl(0, 16, 1, 0x98, ip) +#define NERR_FAT_FBD_RD(ip) nb_pci_getl(0, 16, 1, 0x9c, ip) +#define FERR_NF_FBD_RD(ip) nb_pci_getl(0, 16, 1, 0xa0, ip) +#define NERR_NF_FBD_RD(ip) nb_pci_getl(0, 16, 1, 0xa4, ip) +#define EMASK_FBD_RD() nb_pci_getl(0, 16, 1, 0xa8, 0) +#define ERR0_FBD_RD() nb_pci_getl(0, 16, 1, 0xac, 0) +#define ERR1_FBD_RD() nb_pci_getl(0, 16, 1, 0xb0, 0) +#define ERR2_FBD_RD() nb_pci_getl(0, 16, 1, 0xb4, 0) +#define MCERR_FBD_RD() nb_pci_getl(0, 16, 1, 0xb8, 0) + +#define FERR_FAT_FBD_WR(val) nb_pci_putl(0, 16, 1, 0x98, val) +#define NERR_FAT_FBD_WR(val) nb_pci_putl(0, 16, 1, 0x9c, val) +#define FERR_NF_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xa0, val) +#define NERR_NF_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xa4, val) +#define EMASK_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xa8, val) +#define ERR0_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xac, val) +#define ERR1_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xb0, val) +#define ERR2_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xb4, val) +#define MCERR_FBD_WR(val) nb_pci_putl(0, 16, 1, 0xb8, val) + +#define NRECMEMA_RD() nb_pci_getw(0, 16, 1, 0xbe, 0) +#define NRECMEMB_RD() nb_pci_getw(0, 16, 1, 0xc0, 0) +#define NRECFGLOG_RD() nb_pci_getl(0, 16, 1, 0xc4, 0) +#define NRECFBDA_RD() nb_pci_getl(0, 16, 1, 0xc8, 0) +#define NRECFBDB_RD() nb_pci_getl(0, 16, 1, 0xcc, 0) +#define NRECFBDC_RD() nb_pci_getl(0, 16, 1, 0xd0, 0) +#define NRECFBDD_RD() nb_pci_getl(0, 16, 1, 0xd4, 0) +#define NRECFBDE_RD() nb_pci_getl(0, 16, 1, 0xd8, 0) +#define REDMEMB_RD() nb_pci_getl(0, 16, 1, 0x7c, 0) +#define RECMEMA_RD() nb_pci_getw(0, 16, 1, 0xe2, 0) +#define RECMEMB_RD() nb_pci_getl(0, 16, 1, 0xe4, 0) +#define RECFGLOG_RD() nb_pci_getl(0, 16, 1, 0xe8, 0) +#define RECFBDA_RD() nb_pci_getl(0, 16, 1, 0xec, 0) +#define RECFBDB_RD() nb_pci_getl(0, 16, 1, 0xf0, 0) +#define RECFBDC_RD() nb_pci_getl(0, 16, 1, 0xf4, 0) +#define RECFBDD_RD() nb_pci_getl(0, 16, 1, 0xf8, 0) +#define RECFBDE_RD() nb_pci_getl(0, 16, 1, 0xfc, 0) +#define NRECMEMA_WR() nb_pci_putw(0, 16, 1, 0xbe, 0) +#define NRECMEMB_WR() nb_pci_putw(0, 16, 1, 0xc0, 0) +#define NRECFGLOG_WR() nb_pci_putl(0, 16, 1, 0xc4, 0) +#define NRECFBDA_WR() nb_pci_putl(0, 16, 1, 0xc8, 0) +#define NRECFBDB_WR() nb_pci_putl(0, 16, 1, 0xcc, 0) +#define NRECFBDC_WR() nb_pci_putl(0, 16, 1, 0xd0, 0) +#define NRECFBDD_WR() nb_pci_putl(0, 16, 1, 0xd4, 0) +#define NRECFBDE_WR() nb_pci_putl(0, 16, 1, 0xd8, 0) +#define REDMEMB_WR() nb_pci_putl(0, 16, 1, 0x7c, 0) +#define RECMEMA_WR() nb_pci_putw(0, 16, 1, 0xe2, 0) +#define RECMEMB_WR() nb_pci_putl(0, 16, 1, 0xe4, 0) +#define RECFGLOG_WR() nb_pci_putl(0, 16, 1, 0xe8, 0) +#define RECFBDA_WR() nb_pci_putl(0, 16, 1, 0xec, 0) +#define RECFBDB_WR() nb_pci_putl(0, 16, 1, 0xf0, 0) +#define RECFBDC_WR() nb_pci_putl(0, 16, 1, 0xf4, 0) +#define RECFBDD_WR() nb_pci_putl(0, 16, 1, 0xf8, 0) +#define RECFBDE_WR() nb_pci_putl(0, 16, 1, 0xfc, 0) + +#define MC_RD() nb_pci_getl(0, 16, 1, 0x40, 0) +#define MC_WR(val) nb_pci_putl(0, 16, 1, 0x40, val) +#define MCA_RD() nb_pci_getl(0, 16, 1, 0x58, 0) +#define TOLM_RD() nb_pci_getw(0, 16, 1, 0x6c, 0) + +#define MTR_RD(branch, dimm) ((branch) == 0) ? \ + nb_pci_getw(0, 21, 0, 0x80 + dimm * 4, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getw(0, 22, 0, 0x80 + dimm * 4, 0) : 0 +#define MIR_RD(reg) nb_pci_getw(0, 16, 1, 0x80 + ((reg)*4), 0) + +#define DMIR_RD(branch, reg) \ + ((branch) == 0) ? nb_pci_getl(0, 21, 0, 0x90 + ((reg)*4), 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0x90 + ((reg)*4), 0) : 0 + +#define SPCPC_RD(branch) (nb_dimms_per_channel <= 4 ? \ + (((branch) == 0) ? \ + (uint32_t)nb_pci_getb(0, 21, 0, 0x40, 0) : \ + (nb_number_memory_controllers == 2) ? \ + (uint32_t)nb_pci_getb(0, 22, 0, 0x40, 0) : 0) : \ + nb_pci_getl(0, ((branch) == 0) ? 21 : 22, 0, 0x40, 0)) + +#define SPCPC_SPARE_ENABLE (nb_dimms_per_channel <= 4 ? 1 : 0x20) +#define SPCPC_SPRANK(spcpc) (nb_dimms_per_channel <= 4 ? \ + (((spcpc) >> 1) & 7) : ((spcpc) & 0xf)) + +#define SPCPS_RD(branch) ((branch) == 0) ? \ + nb_pci_getb(0, 21, 0, nb_dimms_per_channel <= 4 ? 0x41 : 0x43, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getb(0, 22, 0, nb_dimms_per_channel <= 4 ? 0x41 : 0x43, 0) : 0 + +#define SPCPS_WR(branch) \ + if ((branch) == 0) { \ + nb_pci_putb(0, 21, 0, nb_dimms_per_channel <= 4 ? 0x41 : 0x43, \ + 0); \ + } else if (nb_number_memory_controllers == 2) { \ + nb_pci_putb(0, 22, 0, nb_dimms_per_channel <= 4 ? 0x41 : 0x43, \ + 0); \ + } + +#define SPCPS_SPARE_DEPLOYED (nb_dimms_per_channel <= 4 ? 0x11 : 0x60) +#define SPCPS_FAILED_RANK(spcps) (nb_dimms_per_channel <= 4 ? \ + (((spcps) >> 1) & 7) : ((spcps) & 0xf)) + +#define UERRCNT_RD(branch) ((branch) == 0) ? \ + nb_pci_getl(0, 21, 0, 0xa4, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0xa4, 0) : 0 +#define CERRCNT_RD(branch) ((branch) == 0) ? \ + nb_pci_getl(0, 21, 0, 0xa8, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0xa8, 0) : 0 +#define BADRAMA_RD(branch) ((branch) == 0) ? \ + nb_pci_getl(0, 21, 0, 0xac, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0xac, 0) : 0 +#define BADRAMB_RD(branch) ((branch) == 0) ? \ + nb_pci_getw(0, 21, 0, 0xb0, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getw(0, 22, 0, 0xb0, 0) : 0 +#define BADCNT_RD(branch) ((branch) == 0) ? \ + nb_pci_getl(0, 21, 0, 0xb4, 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0xb4, 0) : 0 + +#define UERRCNT_WR(branch, val) ((branch) == 0) ? \ + nb_pci_putl(0, 21, 0, 0xa4, val) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_putl(0, 22, 0, 0xa4, val) \ + : 0 +#define CERRCNT_WR(branch, val) ((branch) == 0) ? \ + nb_pci_putl(0, 21, 0, 0xa8, val) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_putl(0, 22, 0, 0xa8, val) : 0 +#define BADRAMA_WR(branch, val) ((branch) == 0) ? \ + nb_pci_putl(0, 21, 0, 0xac, val) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_putl(0, 22, 0, 0xac, val) : 0 +#define BADRAMB_WR(branch, val) ((branch) == 0) ? \ + nb_pci_putw(0, 21, 0, 0xb0, val) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_putw(0, 22, 0, 0xb0) : 0 +#define BADCNT_WR(branch, val) ((branch) == 0) ? \ + nb_pci_putl(0, 21, 0, 0xb4, val) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_putl(0, 22, 0, 0xb4, val) : 0 + +#define SPD_RD(branch, channel) ((branch) == 0) ? \ + nb_pci_getw(0, 21, 0, 0x74 + ((channel) * 2), 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getw(0, 22, 0, 0x74 + ((channel) * 2), 0) : 0 +#define SPDCMDRD(branch, channel) ((branch) == 0) ? \ + nb_pci_getl(0, 21, 0, 0x78 + ((channel) * 4), 0) : \ + (nb_number_memory_controllers == 2) ? \ + nb_pci_getl(0, 22, 0, 0x78 + ((channel) * 4), 0) : 0 + +#define SPDCMD1_1_WR(val) nb_pci_putl(0, 21, 0, 0x7c, val) +#define SPDCMD_WR(branch, channel, val) \ + if ((branch) == 0) \ + nb_pci_putl(0, 21, 0, 0x78 + ((channel) * 4), val); \ + else if (nb_number_memory_controllers == 2) \ + nb_pci_putl(0, 22, 0, 0x78 + ((channel) * 4), val) + +#define UNCERRSTS_RD(pex) nb_pci_getl(0, pex, 0, 0x104, 0) +#define UNCERRMSK_RD(pex) nb_pci_getl(0, pex, 0, 0x108, 0) +#define PEX_FAT_FERR_ESI_RD() nb_pci_getl(0, 0, 0, 0x154, 0) +#define PEX_FAT_NERR_ESI_RD() nb_pci_getl(0, 0, 0, 0x15c, 0) +#define PEX_NF_FERR_ESI_RD() nb_pci_getl(0, 0, 0, 0x158, 0) +#define PEX_NF_NERR_ESI_RD() nb_pci_getl(0, 0, 0, 0x160, 0) +#define PEX_ERR_DOCMD_RD(pex) nb_pci_getl(0, pex, 0, 0x144, 0) +#define EMASK_UNCOR_PEX_RD(pex) nb_pci_getl(0, pex, 0, 0x148, 0) +#define EMASK_COR_PEX_RD(pex) nb_pci_getl(0, pex, 0, 0x14c, 0) +#define EMASK_RP_PEX_RD(pex) nb_pci_getl(0, pex, 0, 0x150, 0) + +#define UNCERRSTS_WR(pex, val) nb_pci_putl(0, pex, 0, 0x104, val) +#define UNCERRMSK_WR(pex, val) nb_pci_putl(0, pex, 0, 0x108, val) +#define PEX_FAT_FERR_ESI_WR(val) nb_pci_putl(0, 0, 0, 0x154, val) +#define PEX_FAT_NERR_ESI_WR(val) nb_pci_putl(0, 0, 0, 0x15c, val) +#define PEX_NF_FERR_ESI_WR(val) nb_pci_putl(0, 0, 0, 0x158, val) +#define PEX_NF_NERR_ESI_WR(val) nb_pci_putl(0, 0, 0, 0x160, val) +#define PEX_ERR_DOCMD_WR(pex, val) nb_pci_putl(0, pex, 0, 0x144, val) +#define EMASK_UNCOR_PEX_WR(pex, val) nb_pci_putl(0, pex, 0, 0x148, val) +#define EMASK_COR_PEX_WR(pex, val) nb_pci_putl(0, pex, 0, 0x14c, val) +#define EMASK_RP_PEX_WR(pex, val) nb_pci_putl(0, pex, 0, 0x150, val) + +#define PEX_FAT_FERR_RD(pex, ip) nb_pci_getl(0, pex, 0, 0x154, ip) +#define PEX_FAT_NERR_RD(pex, ip) nb_pci_getl(0, pex, 0, 0x15c, ip) +#define PEX_NF_FERR_RD(pex, ip) nb_pci_getl(0, pex, 0, 0x158, ip) +#define PEX_NF_NERR_RD(pex, ip) nb_pci_getl(0, pex, 0, 0x160, ip) +#define UNCERRSEV_RD(pex) nb_pci_getl(0, pex, 0, 0x10c, 0) +#define CORERRSTS_RD(pex) nb_pci_getl(0, pex, 0, 0x110, 0) +#define RPERRSTS_RD(pex) nb_pci_getl(0, pex, 0, 0x130, 0) +#define RPERRSID_RD(pex) nb_pci_getl(0, pex, 0, 0x134, 0) +#define AERRCAPCTRL_RD(pex) nb_pci_getl(0, pex, 0, 0x118, 0) +#define PEXDEVSTS_RD(pex) nb_pci_getw(0, pex, 0, 0x76, 0) + +#define PEX_FAT_FERR_WR(pex, val) nb_pci_putl(0, pex, 0, 0x154, val) +#define PEX_FAT_NERR_WR(pex, val) nb_pci_putl(0, pex, 0, 0x15c, val) +#define PEX_NF_FERR_WR(pex, val) nb_pci_putl(0, pex, 0, 0x158, val) +#define PEX_NF_NERR_WR(pex, val) nb_pci_putl(0, pex, 0, 0x160, val) +#define CORERRSTS_WR(pex, val) nb_pci_putl(0, pex, 0, 0x110, val) +#define UNCERRSEV_WR(pex, val) nb_pci_putl(0, pex, 0, 0x10c, val) +#define RPERRSTS_WR(pex, val) nb_pci_putl(0, pex, 0, 0x130, val) +#define PEXDEVSTS_WR(pex, val) nb_pci_putl(0, pex, 0, 0x76, val) + +#define PCISTS_RD(ip) nb_pci_getw(0, 8, 0, 0x6, ip) +#define PCIDEVSTS_RD() nb_pci_getw(0, 8, 0, 0x76, 0) +#define PCISTS_WR(val) nb_pci_putw(0, 8, 0, 0x6, val) +#define PCIDEVSTS_WR(val) nb_pci_putw(0, 8, 0, 0x76, val) + +#define RANK_MASK (nb_dimms_per_channel <= 4 ? 7 : 0xf) +#define CAS_MASK (nb_dimms_per_channel <= 4 ? 0xfff : 0x1fff) +#define RAS_MASK (nb_dimms_per_channel <= 4 ? 0x7fff : 0xffff) +#define BANK_MASK 7 + +#define DMIR_RANKS(dimms_per_channel, dmir, rank0, rank1, rank2, rank3) \ + if ((dimms_per_channel) == 4) { \ + rank0 = (dmir) & 3; \ + rank1 = ((dmir) >> 3) & 3; \ + rank2 = ((dmir) >> 6) & 3; \ + rank3 = ((dmir) >> 9) & 3; \ + } else { \ + rank0 = (dmir) & 0xf; \ + rank1 = ((dmir) >> 4) & 0xf; \ + rank2 = ((dmir) >> 8) & 0xf; \ + rank3 = ((dmir) >> 12) & 0xf; \ + } + +#ifdef __cplusplus +} +#endif + +#endif /* _NB5000_H */ diff --git a/usr/src/uts/i86pc/io/intel_nb5000/nb5000_init.c b/usr/src/uts/i86pc/io/intel_nb5000/nb5000_init.c new file mode 100644 index 0000000000..a5c55b46ae --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/nb5000_init.c @@ -0,0 +1,1059 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/errno.h> +#include <sys/log.h> +#include <sys/systm.h> +#include <sys/modctl.h> +#include <sys/errorq.h> +#include <sys/controlregs.h> +#include <sys/fm/util.h> +#include <sys/fm/protocol.h> +#include <sys/sysevent.h> +#include <sys/pghw.h> +#include <sys/cyclic.h> +#include <sys/pci_cfgspace.h> +#include <sys/mc_intel.h> +#include <sys/cpu_module_impl.h> +#include <sys/smbios.h> +#include <sys/pci.h> +#include <sys/machsystm.h> +#include "nb5000.h" +#include "nb_log.h" +#include "dimm_phys.h" +#include "rank.h" + +int nb_hw_memory_scrub_enable = 1; +static int nb_sw_scrub_disabled = 0; + +int nb_5000_memory_controller = 0; +int nb_number_memory_controllers = NB_5000_MAX_MEM_CONTROLLERS; +int nb_dimms_per_channel = 0; +static int ndimms = 0; + +nb_dimm_t **nb_dimms; +int nb_ndimm; +uint32_t nb_chipset; +enum nb_memory_mode nb_mode; +bank_select_t nb_banks[NB_MEM_BRANCH_SELECT]; +rank_select_t nb_ranks[NB_5000_MAX_MEM_CONTROLLERS][NB_MEM_RANK_SELECT]; +uint32_t top_of_low_memory; +uint8_t spare_rank[NB_5000_MAX_MEM_CONTROLLERS]; + +errorq_t *nb_queue; +kmutex_t nb_mutex; + +static uint8_t nb_err0_int; +static uint8_t nb_err1_int; +static uint8_t nb_err2_int; +static uint8_t nb_mcerr_int; +static uint8_t nb_emask_int; + +static uint32_t nb_err0_fbd; +static uint32_t nb_err1_fbd; +static uint32_t nb_err2_fbd; +static uint32_t nb_mcerr_fbd; +static uint32_t nb_emask_fbd; + +static uint16_t nb_err0_fsb; +static uint16_t nb_err1_fsb; +static uint16_t nb_err2_fsb; +static uint16_t nb_mcerr_fsb; +static uint16_t nb_emask_fsb; + +static uint32_t emask_uncor_pex[NB_PCI_DEV]; +static uint32_t emask_cor_pex[NB_PCI_DEV]; +static uint32_t emask_rp_pex[NB_PCI_DEV]; +static uint32_t docmd_pex[NB_PCI_DEV]; +static uint32_t uncerrsev[NB_PCI_DEV]; + +static uint8_t l_mcerr_int; +static uint32_t l_mcerr_fbd; +static uint16_t l_mcerr_fsb; + +uint_t nb5000_emask_fbd = EMASK_FBD_RES; +int nb5000_reset_emask_fbd = 1; +uint_t nb5000_mask_poll_fbd = EMASK_FBD_NF; +uint_t nb5000_mask_bios_fbd = EMASK_FBD_FATAL; + +uint_t nb5000_emask_fsb = 0; +int nb5000_reset_emask_fsb = 1; +uint_t nb5000_mask_poll_fsb = EMASK_FSB_NF; +uint_t nb5000_mask_bios_fsb = EMASK_FSB_FATAL; + +uint_t nb7300_emask_int = EMASK_INT_7300; +uint_t nb7300_emask_int_step0 = EMASK_INT_7300_STEP_0; +uint_t nb5000_emask_int = EMASK_INT_5000; +int nb5000_reset_emask_int = 1; +uint_t nb5000_mask_poll_int = EMASK_INT_NF; +uint_t nb5000_mask_bios_int = EMASK_INT_FATAL; + +int nb5000_reset_uncor_pex = 0; +uint_t nb5000_mask_uncor_pex = 0; +int nb5000_reset_cor_pex = 1; +uint_t nb5000_mask_cor_pex = 0xffffffff; +uint32_t nb5000_docmd_pex_mask = DOCMD_PEX_MASK; +uint32_t nb5000_docmd_pex = DOCMD_PEX; + +int nb_mask_mc_set; + +static unsigned short +read_spd(int bus) +{ + unsigned short rt = 0; + int branch = bus >> 1; + int channel = bus & 1; + + rt = SPD_RD(branch, channel); + + return (rt); +} + +static void +write_spdcmd(int bus, uint32_t val) +{ + int branch = bus >> 1; + int channel = bus & 1; + SPDCMD_WR(branch, channel, val); +} + +static int +read_spd_eeprom(int bus, int slave, int addr) +{ + int retry = 4; + int wait; + int spd; + uint32_t cmd; + + for (;;) { + wait = 1000; + for (;;) { + spd = read_spd(bus); + if ((spd & SPD_BUSY) == 0) + break; + if (--wait == 0) + return (-1); + drv_usecwait(10); + } + cmd = SPD_EEPROM_WRITE | SPD_ADDR(slave, addr); + write_spdcmd(bus, cmd); + wait = 1000; + for (;;) { + spd = read_spd(bus); + if ((spd & SPD_BUSY) == 0) + break; + if (--wait == 0) { + spd = SPD_BUS_ERROR; + break; + } + drv_usecwait(10); + } + while ((spd & SPD_BUS_ERROR) == 0 && + (spd & (SPD_READ_DATA_VALID|SPD_BUSY)) != + SPD_READ_DATA_VALID) { + spd = read_spd(bus); + if (--wait == 0) + return (-1); + } + if ((spd & SPD_BUS_ERROR) == 0) + break; + if (--retry == 0) + return (-1); + } + return (spd & 0xff); +} + +static void +nb_fini() +{ + int i, j; + int nchannels = nb_number_memory_controllers * 2; + nb_dimm_t **dimmpp; + nb_dimm_t *dimmp; + + dimmpp = nb_dimms; + for (i = 0; i < nchannels; i++) { + for (j = 0; j < nb_dimms_per_channel; j++) { + dimmp = *dimmpp; + if (dimmp) { + kmem_free(dimmp, sizeof (nb_dimm_t)); + *dimmpp = NULL; + } + dimmp++; + } + } + kmem_free(nb_dimms, sizeof (nb_dimm_t *) * + nb_number_memory_controllers * 2 * nb_dimms_per_channel); + nb_dimms = NULL; + dimm_fini(); +} + +void +nb_scrubber_enable() +{ + uint32_t mc; + + if (!nb_hw_memory_scrub_enable) + return; + + mc = MC_RD(); + if ((mc & MC_MIRROR) != 0) /* mirror mode */ + mc |= MC_PATROL_SCRUB; + else + mc |= MC_PATROL_SCRUB|MC_DEMAND_SCRUB; + MC_WR(mc); + + if (nb_sw_scrub_disabled++) + memscrub_disable(); +} + +static nb_dimm_t * +nb_dimm_init(int channel, int dimm, uint16_t mtr) +{ + nb_dimm_t *dp; + int i, t; + int spd_sz; + + t = read_spd_eeprom(channel, dimm, 2) & 0xf; + + if (t != 9) + return (NULL); + + dp = kmem_zalloc(sizeof (nb_dimm_t), KM_SLEEP); + t = read_spd_eeprom(channel, dimm, 0) & 0xf; + if (t == 1) + spd_sz = 128; + else if (t == 2) + spd_sz = 176; + else + spd_sz = 256; + if (mtr & 0x10) + dp->mtr_present = 1; + dp->manufacture_id = read_spd_eeprom(channel, dimm, 117) | + (read_spd_eeprom(channel, dimm, 118) << 8); + dp->manufacture_location = read_spd_eeprom(channel, dimm, 119); + dp->serial_number = + (read_spd_eeprom(channel, dimm, 122) << 24) | + (read_spd_eeprom(channel, dimm, 123) << 16) | + (read_spd_eeprom(channel, dimm, 124) << 8) | + read_spd_eeprom(channel, dimm, 125); + t = read_spd_eeprom(channel, dimm, 121); + dp->manufacture_week = (t >> 4) * 10 + (t & 0xf); + dp->manufacture_year = read_spd_eeprom(channel, dimm, 120); + if (spd_sz > 128) { + for (i = 0; i < sizeof (dp->part_number); i++) { + dp->part_number[i] = + read_spd_eeprom(channel, dimm, 128 + i); + } + for (i = 0; i < sizeof (dp->revision); i++) { + dp->revision[i] = + read_spd_eeprom(channel, dimm, 146 + i); + } + } + dp->nranks = MTR_NUMRANK(mtr); + dp->nbanks = MTR_NUMBANK(mtr); + dp->ncolumn = MTR_NUMCOL(mtr); + dp->nrow = MTR_NUMROW(mtr); + dp->width = MTR_WIDTH(mtr); + dp->dimm_size = MTR_DIMMSIZE(mtr); + + return (dp); +} + +static uint64_t +mc_range(int controller, uint64_t base) +{ + int i; + uint64_t limit = 0; + + for (i = 0; i < NB_MEM_BRANCH_SELECT; i++) { + if (nb_banks[i].way[controller] && base >= nb_banks[i].base && + base < nb_banks[i].limit) { + limit = nb_banks[i].limit; + if (base <= top_of_low_memory && + limit > top_of_low_memory) { + limit -= TLOW_MAX - top_of_low_memory; + } + if (nb_banks[i].way[0] && nb_banks[i].way[1] && + nb_mode != NB_MEMORY_MIRROR) { + limit = limit / 2; + } + } + } + return (limit); +} + +void +nb_mc_init() +{ + uint16_t tolm; + uint16_t mir; + uint32_t hole_base; + uint32_t hole_size; + uint32_t dmir; + uint64_t base; + uint64_t limit; + uint8_t way0, way1, rank0, rank1, rank2, rank3, branch_interleave; + int i, j, k; + uint8_t interleave; + + base = 0; + tolm = TOLM_RD(); + top_of_low_memory = ((uint32_t)(tolm >> 12) & 0xf) << 28; + for (i = 0; i < NB_MEM_BRANCH_SELECT; i++) { + mir = MIR_RD(i); + limit = (uint64_t)(mir >> 4) << 28; + way0 = mir & 1; + way1 = (mir >> 1) & 1; + if (way0 == 0 && way1 == 0) { + way0 = 1; + way1 = 1; + } + if (limit > top_of_low_memory) + limit += TLOW_MAX - top_of_low_memory; + nb_banks[i].base = base; + nb_banks[i].limit = limit; + nb_banks[i].way[0] = way0; + nb_banks[i].way[1] = way1; + base = limit; + } + for (i = 0; i < nb_number_memory_controllers; i++) { + base = 0; + + for (j = 0; j < NB_MEM_RANK_SELECT; j++) { + dmir = DMIR_RD(i, j); + limit = ((uint64_t)(dmir >> 16) & 0xff) << 28; + if (limit == 0) { + limit = mc_range(i, base); + } + branch_interleave = 0; + hole_base = 0; + hole_size = 0; + DMIR_RANKS(nb_dimms_per_channel, dmir, rank0, rank1, + rank2, rank3); + if (rank0 == rank1) + interleave = 1; + else if (rank0 == rank2) + interleave = 2; + else + interleave = 4; + if (nb_mode != NB_MEMORY_MIRROR && + nb_mode != NB_MEMORY_SINGLE_CHANNEL) { + for (k = 0; k < NB_MEM_BRANCH_SELECT; k++) { + if (base >= nb_banks[k].base && + base < nb_banks[k].limit) { + if (nb_banks[i].way[0] && + nb_banks[i].way[1]) { + interleave *= 2; + limit *= 2; + branch_interleave = 1; + } + break; + } + } + } + if (base < top_of_low_memory && + limit > top_of_low_memory) { + hole_base = top_of_low_memory; + hole_size = TLOW_MAX - top_of_low_memory; + limit += hole_size; + } else if (base > top_of_low_memory) { + limit += TLOW_MAX - top_of_low_memory; + } + nb_ranks[i][j].base = base; + nb_ranks[i][j].limit = limit; + nb_ranks[i][j].rank[0] = rank0; + nb_ranks[i][j].rank[1] = rank1; + nb_ranks[i][j].rank[2] = rank2; + nb_ranks[i][j].rank[3] = rank3; + nb_ranks[i][j].interleave = interleave; + nb_ranks[i][j].branch_interleave = branch_interleave; + nb_ranks[i][j].hole_base = hole_base; + nb_ranks[i][j].hole_size = hole_size; + if (limit > base) { + dimm_add_rank(i, rank0, branch_interleave, 0, + base, hole_base, hole_size, interleave, + limit); + if (rank0 != rank1) { + dimm_add_rank(i, rank1, + branch_interleave, 1, base, + hole_base, hole_size, interleave, + limit); + if (rank0 != rank2) { + dimm_add_rank(i, rank2, + branch_interleave, 2, base, + hole_base, hole_size, + interleave, limit); + dimm_add_rank(i, rank3, + branch_interleave, 3, base, + hole_base, hole_size, + interleave, limit); + } + } + } + base = limit; + } + } +} + +void +nb_used_spare_rank(int branch, int bad_rank) +{ + int i; + int j; + + for (i = 0; i < NB_MEM_RANK_SELECT; i++) { + for (j = 0; j < NB_RANKS_IN_SELECT; j++) { + if (nb_ranks[branch][i].rank[j] == bad_rank) { + nb_ranks[branch][i].rank[j] = + spare_rank[branch]; + i = NB_MEM_RANK_SELECT; + break; + } + } + } +} + +/*ARGSUSED*/ +static int +memoryarray(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg) +{ + smbios_memarray_t ma; + + if (sp->smbstr_type == SMB_TYPE_MEMARRAY && + smbios_info_memarray(shp, sp->smbstr_id, &ma) == 0) { + ndimms += ma.smbma_ndevs; + } + return (0); +} + +void +find_dimms_per_channel() +{ + if (nb_dimms_per_channel == 0) { + if (ksmbios != NULL) { + (void) smbios_iter(ksmbios, memoryarray, 0); + nb_dimms_per_channel = ndimms / + (nb_number_memory_controllers * 2); + } + if (nb_dimms_per_channel == 0) { + if (nb_chipset == INTEL_NB_7300) + nb_dimms_per_channel = 8; + else + nb_dimms_per_channel = 4; + } + } +} + +static int +dimm_label(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg) +{ + nb_dimm_t ***dimmpp = arg; + nb_dimm_t *dimmp; + smbios_memdevice_t md; + + if (sp->smbstr_type == SMB_TYPE_MEMDEVICE) { + dimmp = **dimmpp; + if (dimmp && smbios_info_memdevice(shp, sp->smbstr_id, + &md) == 0 && md.smbmd_dloc != NULL) { + (void) snprintf(dimmp->label, + sizeof (dimmp->label), "%s", md.smbmd_dloc); + } + (*dimmpp)++; + } + return (0); +} + +void +nb_smbios() +{ + nb_dimm_t **dimmpp; + + if (ksmbios != NULL) { + dimmpp = nb_dimms; + (void) smbios_iter(ksmbios, dimm_label, &dimmpp); + } +} + +static void +nb_dimms_init() +{ + int i, j, k; + uint16_t mtr; + uint32_t mc, mca; + uint32_t spcpc; + uint8_t spcps; + nb_dimm_t **dimmpp; + + mca = MCA_RD(); + mc = MC_RD(); + if (mca & MCA_SCHDIMM) /* single-channel mode */ + nb_mode = NB_MEMORY_SINGLE_CHANNEL; + else if ((mc & MC_MIRROR) != 0) /* mirror mode */ + nb_mode = NB_MEMORY_MIRROR; + else + nb_mode = NB_MEMORY_NORMAL; + nb_dimms = (nb_dimm_t **)kmem_zalloc(sizeof (nb_dimm_t *) * + nb_number_memory_controllers * 2 * nb_dimms_per_channel, KM_SLEEP); + dimmpp = nb_dimms; + for (i = 0; i < nb_number_memory_controllers; i++) { + if (nb_mode == NB_MEMORY_NORMAL) { + spcpc = SPCPC_RD(i); + spcps = SPCPS_RD(i); + if ((spcpc & SPCPC_SPARE_ENABLE) != 0 && + (spcps & SPCPS_SPARE_DEPLOYED) != 0) + nb_mode = NB_MEMORY_SPARE_RANK; + spare_rank[i] = SPCPC_SPRANK(spcpc); + } + for (j = 0; j < nb_dimms_per_channel; j++) { + mtr = MTR_RD(i, j); + k = i * 2; + dimmpp[j] = nb_dimm_init(k, j, mtr); + if (dimmpp[j]) { + nb_ndimm ++; + dimm_add_geometry(i, j, dimmpp[j]->nbanks, + dimmpp[j]->width, dimmpp[j]->ncolumn, + dimmpp[j]->nrow); + } + dimmpp[j + nb_dimms_per_channel] = + nb_dimm_init(k + 1, j, mtr); + if (dimmpp[j + nb_dimms_per_channel]) + nb_ndimm ++; + } + dimmpp += nb_dimms_per_channel * 2; + } + nb_smbios(); +} + +static void +nb_pex_init() +{ + int i; + + for (i = 0; i < NB_PCI_DEV; i++) { + switch (nb_chipset) { + case INTEL_NB_5000P: + case INTEL_NB_5000X: + if (i == 1) + continue; + break; + case INTEL_NB_5000V: + if (i == 1 || i > 3) + continue; + break; + case INTEL_NB_5000Z: + if (i == 1 || i > 5) + continue; + break; + case INTEL_NB_7300: + break; + } + emask_uncor_pex[i] = EMASK_UNCOR_PEX_RD(i); + emask_cor_pex[i] = EMASK_COR_PEX_RD(i); + emask_rp_pex[i] = EMASK_RP_PEX_RD(i); + docmd_pex[i] = PEX_ERR_DOCMD_RD(i); + uncerrsev[i] = UNCERRSEV_RD(i); + + if (nb5000_reset_uncor_pex) + EMASK_UNCOR_PEX_WR(i, nb5000_mask_uncor_pex); + if (nb5000_reset_cor_pex) + EMASK_COR_PEX_WR(i, nb5000_mask_cor_pex); + PEX_ERR_DOCMD_WR(i, (docmd_pex[i] & nb5000_docmd_pex_mask) | + (nb5000_docmd_pex & ~nb5000_docmd_pex_mask)); + } +} + +static void +nb_pex_fini() +{ + int i; + + for (i = 0; i < NB_PCI_DEV; i++) { + switch (nb_chipset) { + case INTEL_NB_5000P: + case INTEL_NB_5000X: + if (i == 1) + continue; + break; + case INTEL_NB_5000V: + if (i == 1 || i > 3) + continue; + break; + case INTEL_NB_5000Z: + if (i == 1 || i > 5) + continue; + break; + case INTEL_NB_7300: + break; + } + EMASK_UNCOR_PEX_WR(i, emask_uncor_pex[i]); + EMASK_COR_PEX_WR(i, emask_cor_pex[i]); + EMASK_RP_PEX_WR(i, emask_rp_pex[i]); + PEX_ERR_DOCMD_WR(i, docmd_pex[i]); + UNCERRSEV_WR(i, uncerrsev[i]); + + if (nb5000_reset_uncor_pex) + EMASK_UNCOR_PEX_WR(i, nb5000_mask_uncor_pex); + if (nb5000_reset_cor_pex) + EMASK_COR_PEX_WR(i, nb5000_mask_cor_pex); + } +} + +void +nb_int_init() +{ + uint8_t err0_int; + uint8_t err1_int; + uint8_t err2_int; + uint8_t mcerr_int; + uint8_t emask_int; + uint16_t stepping; + + err0_int = ERR0_INT_RD(); + err1_int = ERR1_INT_RD(); + err2_int = ERR2_INT_RD(); + mcerr_int = MCERR_INT_RD(); + emask_int = EMASK_INT_RD(); + + nb_err0_int = err0_int; + nb_err1_int = err1_int; + nb_err2_int = err2_int; + nb_mcerr_int = mcerr_int; + nb_emask_int = emask_int; + + ERR0_INT_WR(0xff); + ERR1_INT_WR(0xff); + ERR2_INT_WR(0xff); + MCERR_INT_WR(0xff); + EMASK_INT_WR(0xff); + + mcerr_int &= ~nb5000_mask_bios_int; + mcerr_int |= nb5000_mask_bios_int & (~err0_int | ~err1_int | ~err2_int); + mcerr_int |= nb5000_mask_poll_int; + err0_int |= nb5000_mask_poll_int; + err1_int |= nb5000_mask_poll_int; + err2_int |= nb5000_mask_poll_int; + + l_mcerr_int = mcerr_int; + ERR0_INT_WR(err0_int); + ERR1_INT_WR(err1_int); + ERR2_INT_WR(err2_int); + MCERR_INT_WR(mcerr_int); + if (nb5000_reset_emask_int) { + if (nb_chipset == INTEL_NB_7300) { + stepping = NB5000_STEPPING(); + if (stepping == 0) + EMASK_INT_WR(nb7300_emask_int_step0); + else + EMASK_INT_WR(nb7300_emask_int); + } else { + EMASK_INT_WR(nb5000_emask_int); + } + } else { + EMASK_INT_WR(nb_emask_int); + } +} + +void +nb_int_fini() +{ + ERR0_INT_WR(0xff); + ERR1_INT_WR(0xff); + ERR2_INT_WR(0xff); + MCERR_INT_WR(0xff); + EMASK_INT_WR(0xff); + + ERR0_INT_WR(nb_err0_int); + ERR1_INT_WR(nb_err1_int); + ERR2_INT_WR(nb_err2_int); + MCERR_INT_WR(nb_mcerr_int); + EMASK_INT_WR(nb_emask_int); +} + +void +nb_int_mask_mc(uint8_t mc_mask_int) +{ + uint8_t emask_int; + + emask_int = MCERR_INT_RD(); + if ((emask_int & mc_mask_int) != mc_mask_int) { + MCERR_INT_WR(emask_int|mc_mask_int); + nb_mask_mc_set = 1; + } +} + +void +nb_fbd_init() +{ + uint32_t err0_fbd; + uint32_t err1_fbd; + uint32_t err2_fbd; + uint32_t mcerr_fbd; + uint32_t emask_fbd; + + err0_fbd = ERR0_FBD_RD(); + err1_fbd = ERR1_FBD_RD(); + err2_fbd = ERR2_FBD_RD(); + mcerr_fbd = MCERR_FBD_RD(); + emask_fbd = EMASK_FBD_RD(); + + nb_err0_fbd = err0_fbd; + nb_err1_fbd = err1_fbd; + nb_err2_fbd = err2_fbd; + nb_mcerr_fbd = mcerr_fbd; + nb_emask_fbd = emask_fbd; + + ERR0_FBD_WR(0xffffffff); + ERR1_FBD_WR(0xffffffff); + ERR2_FBD_WR(0xffffffff); + MCERR_FBD_WR(0xffffffff); + EMASK_FBD_WR(0xffffffff); + + if (nb_chipset == INTEL_NB_7300 && nb_mode == NB_MEMORY_MIRROR) { + /* MCH 7300 errata 34 */ + nb5000_mask_bios_fbd &= ~EMASK_FBD_M23; + mcerr_fbd |= EMASK_FBD_M23; + } + mcerr_fbd &= ~nb5000_mask_bios_fbd; + mcerr_fbd |= nb5000_mask_bios_fbd & (~err0_fbd | ~err1_fbd | ~err2_fbd); + mcerr_fbd |= nb5000_mask_poll_fbd; + err0_fbd |= nb5000_mask_poll_fbd; + err1_fbd |= nb5000_mask_poll_fbd; + err2_fbd |= nb5000_mask_poll_fbd; + + l_mcerr_fbd = mcerr_fbd; + ERR0_FBD_WR(err0_fbd); + ERR1_FBD_WR(err1_fbd); + ERR2_FBD_WR(err2_fbd); + MCERR_FBD_WR(mcerr_fbd); + if (nb5000_reset_emask_fbd) + EMASK_FBD_WR(nb5000_emask_fbd); + else + EMASK_FBD_WR(nb_emask_fbd); +} + +void +nb_fbd_mask_mc(uint32_t mc_mask_fbd) +{ + uint32_t emask_fbd; + + emask_fbd = MCERR_FBD_RD(); + if ((emask_fbd & mc_mask_fbd) != mc_mask_fbd) { + MCERR_FBD_WR(emask_fbd|mc_mask_fbd); + nb_mask_mc_set = 1; + } +} + +void +nb_fbd_fini() +{ + ERR0_FBD_WR(0xffffffff); + ERR1_FBD_WR(0xffffffff); + ERR2_FBD_WR(0xffffffff); + MCERR_FBD_WR(0xffffffff); + EMASK_FBD_WR(0xffffffff); + + ERR0_FBD_WR(nb_err0_fbd); + ERR1_FBD_WR(nb_err1_fbd); + ERR2_FBD_WR(nb_err2_fbd); + MCERR_FBD_WR(nb_mcerr_fbd); + EMASK_FBD_WR(nb_emask_fbd); +} + +static void +nb_fsb_init() +{ + uint16_t err0_fsb; + uint16_t err1_fsb; + uint16_t err2_fsb; + uint16_t mcerr_fsb; + uint16_t emask_fsb; + + err0_fsb = ERR0_FSB_RD(0); + err1_fsb = ERR1_FSB_RD(0); + err2_fsb = ERR2_FSB_RD(0); + mcerr_fsb = MCERR_FSB_RD(0); + emask_fsb = EMASK_FSB_RD(0); + + ERR0_FSB_WR(0, 0xffff); + ERR1_FSB_WR(0, 0xffff); + ERR2_FSB_WR(0, 0xffff); + MCERR_FSB_WR(0, 0xffff); + EMASK_FSB_WR(0, 0xffff); + + ERR0_FSB_WR(1, 0xffff); + ERR1_FSB_WR(1, 0xffff); + ERR2_FSB_WR(1, 0xffff); + MCERR_FSB_WR(1, 0xffff); + EMASK_FSB_WR(1, 0xffff); + + nb_err0_fsb = err0_fsb; + nb_err1_fsb = err1_fsb; + nb_err2_fsb = err2_fsb; + nb_mcerr_fsb = mcerr_fsb; + nb_emask_fsb = emask_fsb; + + mcerr_fsb &= ~nb5000_mask_bios_fsb; + mcerr_fsb |= nb5000_mask_bios_fsb & (~err2_fsb | ~err1_fsb | ~err0_fsb); + mcerr_fsb |= nb5000_mask_poll_fsb; + err0_fsb |= nb5000_mask_poll_fsb; + err1_fsb |= nb5000_mask_poll_fsb; + err2_fsb |= nb5000_mask_poll_fsb; + + l_mcerr_fsb = mcerr_fsb; + ERR0_FSB_WR(0, err0_fsb); + ERR1_FSB_WR(0, err1_fsb); + ERR2_FSB_WR(0, err2_fsb); + MCERR_FSB_WR(0, mcerr_fsb); + if (nb5000_reset_emask_fsb) + EMASK_FSB_WR(0, nb5000_emask_fsb); + else + EMASK_FSB_WR(0, nb_emask_fsb); + + ERR0_FSB_WR(1, err0_fsb); + ERR1_FSB_WR(1, err1_fsb); + ERR2_FSB_WR(1, err2_fsb); + MCERR_FSB_WR(1, mcerr_fsb); + if (nb5000_reset_emask_fsb) + EMASK_FSB_WR(1, nb5000_emask_fsb); + else + EMASK_FSB_WR(1, nb_emask_fsb); + + if (nb_chipset == INTEL_NB_7300) { + ERR0_FSB_WR(2, 0xffff); + ERR1_FSB_WR(2, 0xffff); + ERR2_FSB_WR(2, 0xffff); + MCERR_FSB_WR(2, 0xffff); + EMASK_FSB_WR(2, 0xffff); + + ERR0_FSB_WR(3, 0xffff); + ERR1_FSB_WR(3, 0xffff); + ERR2_FSB_WR(3, 0xffff); + MCERR_FSB_WR(3, 0xffff); + EMASK_FSB_WR(3, 0xffff); + + ERR0_FSB_WR(2, err0_fsb); + ERR1_FSB_WR(2, err1_fsb); + ERR2_FSB_WR(2, err2_fsb); + MCERR_FSB_WR(2, mcerr_fsb); + if (nb5000_reset_emask_fsb) + EMASK_FSB_WR(2, nb5000_emask_fsb); + else + EMASK_FSB_WR(2, nb_emask_fsb); + + ERR0_FSB_WR(3, err0_fsb); + ERR1_FSB_WR(3, err1_fsb); + ERR2_FSB_WR(3, err2_fsb); + MCERR_FSB_WR(3, mcerr_fsb); + if (nb5000_reset_emask_fsb) + EMASK_FSB_WR(3, nb5000_emask_fsb); + else + EMASK_FSB_WR(3, nb_emask_fsb); + } +} + +static void +nb_fsb_fini() { + ERR0_FSB_WR(0, 0xffff); + ERR1_FSB_WR(0, 0xffff); + ERR2_FSB_WR(0, 0xffff); + MCERR_FSB_WR(0, 0xffff); + EMASK_FSB_WR(0, 0xffff); + + ERR0_FSB_WR(0, nb_err0_fsb); + ERR1_FSB_WR(0, nb_err1_fsb); + ERR2_FSB_WR(0, nb_err2_fsb); + MCERR_FSB_WR(0, nb_mcerr_fsb); + EMASK_FSB_WR(0, nb_emask_fsb); + + ERR0_FSB_WR(1, 0xffff); + ERR1_FSB_WR(1, 0xffff); + ERR2_FSB_WR(1, 0xffff); + MCERR_FSB_WR(1, 0xffff); + EMASK_FSB_WR(1, 0xffff); + + ERR0_FSB_WR(1, nb_err0_fsb); + ERR1_FSB_WR(1, nb_err1_fsb); + ERR2_FSB_WR(1, nb_err2_fsb); + MCERR_FSB_WR(1, nb_mcerr_fsb); + EMASK_FSB_WR(1, nb_emask_fsb); + + if (nb_chipset == INTEL_NB_7300) { + ERR0_FSB_WR(2, 0xffff); + ERR1_FSB_WR(2, 0xffff); + ERR2_FSB_WR(2, 0xffff); + MCERR_FSB_WR(2, 0xffff); + EMASK_FSB_WR(2, 0xffff); + + ERR0_FSB_WR(2, nb_err0_fsb); + ERR1_FSB_WR(2, nb_err1_fsb); + ERR2_FSB_WR(2, nb_err2_fsb); + MCERR_FSB_WR(2, nb_mcerr_fsb); + EMASK_FSB_WR(2, nb_emask_fsb); + + ERR0_FSB_WR(3, 0xffff); + ERR1_FSB_WR(3, 0xffff); + ERR2_FSB_WR(3, 0xffff); + MCERR_FSB_WR(3, 0xffff); + EMASK_FSB_WR(3, 0xffff); + + ERR0_FSB_WR(3, nb_err0_fsb); + ERR1_FSB_WR(3, nb_err1_fsb); + ERR2_FSB_WR(3, nb_err2_fsb); + MCERR_FSB_WR(3, nb_mcerr_fsb); + EMASK_FSB_WR(3, nb_emask_fsb); + } +} + +void +nb_fsb_mask_mc(int fsb, uint16_t mc_mask_fsb) +{ + uint16_t emask_fsb; + + emask_fsb = MCERR_FSB_RD(fsb); + if ((emask_fsb & mc_mask_fsb) != mc_mask_fsb) { + MCERR_FSB_WR(fsb, emask_fsb|mc_mask_fsb|EMASK_FBD_RES); + nb_mask_mc_set = 1; + } +} + +void +nb_mask_mc_reset() +{ + MCERR_FBD_WR(l_mcerr_fbd); + MCERR_INT_WR(l_mcerr_int); + MCERR_FSB_WR(0, l_mcerr_fsb); + MCERR_FSB_WR(1, l_mcerr_fsb); + if (nb_chipset == INTEL_NB_7300) { + MCERR_FSB_WR(2, l_mcerr_fsb); + MCERR_FSB_WR(3, l_mcerr_fsb); + } +} + +int +nb_dev_init() +{ + find_dimms_per_channel(); + mutex_init(&nb_mutex, NULL, MUTEX_DRIVER, NULL); + nb_queue = errorq_create("nb_queue", nb_drain, NULL, NB_MAX_ERRORS, + sizeof (nb_logout_t), 1, ERRORQ_VITAL); + if (nb_queue == NULL) { + mutex_destroy(&nb_mutex); + return (EAGAIN); + } + dimm_init(); + nb_dimms_init(); + nb_mc_init(); + nb_pex_init(); + nb_int_init(); + nb_fbd_init(); + nb_fsb_init(); + nb_scrubber_enable(); + return (0); +} + +int +nb_init() +{ + /* get vendor and device */ + nb_chipset = (*pci_getl_func)(0, 0, 0, PCI_CONF_VENID); + switch (nb_chipset) { + default: + if (nb_5000_memory_controller == 0) + return (ENOTSUP); + break; + case INTEL_NB_7300: + case INTEL_NB_5000P: + case INTEL_NB_5000X: + break; + case INTEL_NB_5000V: + case INTEL_NB_5000Z: + nb_number_memory_controllers = 1; + break; + } + return (0); +} + +void +nb_dev_reinit() +{ + int i, j; + int nchannels = nb_number_memory_controllers * 2; + nb_dimm_t **dimmpp; + nb_dimm_t *dimmp; + nb_dimm_t **old_nb_dimms; + int old_nb_dimms_per_channel; + + old_nb_dimms = nb_dimms; + old_nb_dimms_per_channel = nb_dimms_per_channel; + + dimm_fini(); + find_dimms_per_channel(); + dimm_init(); + nb_dimms_init(); + nb_mc_init(); + nb_pex_init(); + nb_int_init(); + nb_fbd_init(); + nb_fsb_init(); + nb_scrubber_enable(); + + dimmpp = old_nb_dimms; + for (i = 0; i < nchannels; i++) { + for (j = 0; j < old_nb_dimms_per_channel; j++) { + dimmp = *dimmpp; + if (dimmp) { + kmem_free(dimmp, sizeof (nb_dimm_t)); + *dimmpp = NULL; + } + dimmp++; + } + } + kmem_free(old_nb_dimms, sizeof (nb_dimm_t *) * + nb_number_memory_controllers * 2 * old_nb_dimms_per_channel); +} + +void +nb_dev_unload() +{ + errorq_destroy(nb_queue); + nb_queue = NULL; + mutex_destroy(&nb_mutex); + nb_int_fini(); + nb_fbd_fini(); + nb_fsb_fini(); + nb_pex_fini(); + nb_fini(); +} + +void +nb_unload() +{ +} diff --git a/usr/src/uts/i86pc/io/intel_nb5000/nb_log.h b/usr/src/uts/i86pc/io/intel_nb5000/nb_log.h new file mode 100644 index 0000000000..bdd68b029d --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/nb_log.h @@ -0,0 +1,269 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NB_LOG_H +#define _NB_LOG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/cpu_module.h> +#include "nb5000.h" + +#define NB_MAX_ERRORS 4 + +/* North Bridge front side bus error registers */ + +typedef struct nb_fsb_regs { + uint8_t fsb; /* cpu slot */ + uint8_t ferr_fat_fsb; + uint8_t nerr_fat_fsb; + uint8_t ferr_nf_fsb; + uint8_t nerr_nf_fsb; + uint64_t nrecfsb_addr; + uint32_t nrecfsb; + uint32_t recfsb; +} nb_fsb_regs_t; + +/* PCI express ESI (South Bridge) error registers */ + +typedef struct nb_pex_regs { + uint8_t pex; /* pci express slot */ + uint32_t pex_fat_ferr; + uint32_t pex_fat_nerr; + uint32_t pex_nf_corr_ferr; + uint32_t pex_nf_corr_nerr; + uint32_t uncerrsev; /* uncorrectable error severity */ + uint32_t rperrsts; /* root error status */ + uint32_t rperrsid; /* error source identification */ + uint32_t uncerrsts; /* uncorrectable error status */ + uint32_t aerrcapctrl; /* advanced error capabilities and control */ + uint32_t corerrsts; /* correctable error status */ + uint16_t pexdevsts; /* pci express device status */ +} nb_pex_regs_t; + +/* North Bridge memory controller hub internal error registers */ + +typedef struct nb_int { + uint8_t ferr_fat_int; /* first fatal error */ + uint8_t ferr_nf_int; /* first non-fatal error */ + uint8_t nerr_fat_int; /* next fatal error */ + uint8_t nerr_nf_int; /* next non-fatal error */ + uint32_t nrecint; /* non recoverable error log */ + uint32_t recint; /* recoverable error log */ + uint64_t nrecsf; /* non recoverable control information */ + uint64_t recsf; /* recoverable control information */ +} nb_int_t; + +/* memory errors */ + +typedef struct nb_fat_fbd { + uint32_t ferr_fat_fbd; /* fb-dimm first fatal error */ + uint32_t nerr_fat_fbd; /* fb-dimm next fatal error */ + uint16_t nrecmema; /* non recoverable memory error log */ + uint32_t nrecmemb; /* non recoverable memory error log */ + uint32_t nrecfglog; /* non recoverable dimm configuration */ + uint32_t nrecfbda; /* non recoverable dimm log A */ + uint32_t nrecfbdb; /* non recoverable dimm log B */ + uint32_t nrecfbdc; /* non recoverable dimm log C */ + uint32_t nrecfbdd; /* non recoverable dimm log D */ + uint32_t nrecfbde; /* non recoverable dimm log E */ + uint32_t spcpc; /* spare copy control */ + uint8_t spcps; /* spare copy status */ + uint32_t uerrcnt; /* uncorrectable error count */ + uint32_t uerrcnt_last; /* saved copy of uncorrectable error count */ + uint32_t badrama; /* bad dram marker A */ + uint16_t badramb; /* bad dram marker B */ + uint32_t badcnt; /* bad dram counter */ +} nb_fat_fbd_t; + +typedef struct nb_nf_fbd { + uint32_t ferr_nf_fbd; /* fb-dimm first non-fatal error */ + uint32_t nerr_nf_fbd; /* fb-dimm next non-fatal error */ + uint32_t redmemb; /* recoverable dimm data error log */ + uint16_t recmema; /* recoverable memory error log A */ + uint32_t recmemb; /* recoverable memory error log B */ + uint32_t recfglog; /* recoverable dimm configuration */ + uint32_t recfbda; /* recoverable dimm log A */ + uint32_t recfbdb; /* recoverable dimm log B */ + uint32_t recfbdc; /* recoverable dimm log C */ + uint32_t recfbdd; /* recoverable dimm log D */ + uint32_t recfbde; /* recoverable dimm log E */ + uint32_t spcpc; /* spare copy control */ + uint8_t spcps; /* spare copy status */ + uint32_t cerrcnt; /* correctable error count */ + uint32_t cerrcnt_last; /* saved copy of correctable error count */ + uint32_t badrama; /* bad dram marker A */ + uint16_t badramb; /* bad dram marker B */ + uint32_t badcnt; /* bad dram counter */ +} nb_nf_fbd_t; + +typedef struct nb_dma { + uint16_t pcists; + uint16_t pexdevsts; +} nb_dma_t; + +typedef struct nb_regs { + int flag; + uint32_t chipset; + uint64_t ferr; + uint32_t nerr; + union { + nb_fsb_regs_t fsb_regs; + nb_pex_regs_t pex_regs; + nb_int_t int_regs; + nb_fat_fbd_t fat_fbd_regs; + nb_nf_fbd_t nf_fbd_regs; + nb_dma_t dma_regs; + } nb; +} nb_regs_t; + +#define NB_REG_LOG_FREE 0 +#define NB_REG_LOG_FSB 1 +#define NB_REG_LOG_PEX 2 +#define NB_REG_LOG_INT 3 +#define NB_REG_LOG_FAT_FBD 4 +#define NB_REG_LOG_NF_FBD 5 +#define NB_REG_LOG_DMA 6 + +typedef struct nb_logout { + uint64_t acl_timestamp; + char *type; + nb_regs_t nb_regs; +} nb_logout_t; + +typedef struct nb_mem_scatchpad { + int intel_error_list; /* error number in Chipset Error List */ + int branch; + int channel; + int rank; + int dimm; + int bank; + int cas; + int ras; + uint64_t offset; + uint64_t pa; +} nb_mem_scatchpad_t; + +typedef union nb_scatchpad { + nb_mem_scatchpad_t ms; + int intel_error_list; /* error number in Chipset Error List */ +} nb_scatchpad_t; + +typedef struct nb_dimm { + uint64_t dimm_size; + uint8_t mtr_present; + uint8_t nranks; + uint8_t nbanks; + uint8_t ncolumn; + uint8_t nrow; + uint8_t width; + uint8_t manufacture_location; + uint8_t manufacture_week; + uint8_t manufacture_year; /* years from 2000 */ + uint16_t manufacture_id; + uint32_t serial_number; + char part_number[16]; + char revision[2]; + char label[64]; +} nb_dimm_t; + +typedef struct bank_select { + uint64_t base; + uint64_t limit; + uint8_t way[2]; +} bank_select_t; + +typedef struct rank_select { + uint64_t base; + uint64_t limit; + uint32_t hole_base; + uint32_t hole_size; + uint8_t rank[4]; + uint8_t interleave; + uint8_t branch_interleave; +} rank_select_t; + +enum nb_memory_mode { NB_MEMORY_SINGLE_CHANNEL, NB_MEMORY_NORMAL, + NB_MEMORY_SPARE_RANK, NB_MEMORY_MIRROR }; + +extern int nb_5000_memory_controller; +extern int nb_number_memory_controllers; +extern int nb_dimms_per_channel; + +extern nb_dimm_t **nb_dimms; +extern uint32_t nb_chipset; + +extern int nb_init(void); +extern int nb_dev_init(void); +extern void nb_dev_reinit(void); +extern void nb_unload(void); +extern void nb_dev_unload(void); +extern uint32_t top_of_low_memory; +extern bank_select_t nb_banks[NB_MEM_BRANCH_SELECT]; +extern rank_select_t nb_ranks[NB_5000_MAX_MEM_CONTROLLERS][NB_MEM_RANK_SELECT]; +extern uint8_t spare_rank[NB_5000_MAX_MEM_CONTROLLERS]; +extern enum nb_memory_mode nb_mode; + +extern int inb_mc_register(cmi_hdl_t, void *, void *, void *); +extern void nb_scrubber_enable(void); +extern void nb_error_trap(cmi_hdl_t, boolean_t, boolean_t); + +extern void nb_pci_cfg_setup(dev_info_t *); +extern void nb_pci_cfg_free(void); + +extern void *ras_regs; + +extern uint8_t nb_pci_getb(int, int, int, int, int *); +extern uint16_t nb_pci_getw(int, int, int, int, int *); +extern uint32_t nb_pci_getl(int, int, int, int, int *); +extern void nb_pci_putb(int, int, int, int, uint8_t); +extern void nb_pci_putw(int, int, int, int, uint16_t); +extern void nb_pci_putl(int, int, int, int, uint32_t); + +extern void nb_fsb_mask_mc(int, uint16_t); +extern void nb_fbd_mask_mc(uint32_t); +extern void nb_int_mask_mc(uint8_t); +extern void nb_mask_mc_reset(void); + +extern int nb_mask_mc_set; + +extern errorq_t *nb_queue; +extern kmutex_t nb_mutex; + +extern void nb_drain(void *, const void *, const errorq_elem_t *); +extern void nb_used_spare_rank(int, int); + +extern uint_t nb_config_gen; + +#ifdef __cplusplus +} +#endif + +#endif /* _NB_LOG_H */ diff --git a/usr/src/uts/i86pc/io/intel_nb5000/nb_pci_cfg.c b/usr/src/uts/i86pc/io/intel_nb5000/nb_pci_cfg.c new file mode 100644 index 0000000000..f6e34d1b2b --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/nb_pci_cfg.c @@ -0,0 +1,180 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/errno.h> +#include <sys/systm.h> +#include <sys/sunddi.h> +#include <sys/pci_cfgspace.h> +#include <sys/pci.h> +#include <sys/pcie.h> +#include <vm/seg_kmem.h> +#include <sys/machparam.h> +#include <sys/mman.h> +#include <sys/cpu_module.h> +#include "nb5000.h" + +static ddi_acc_handle_t dev_16_hdl[NB_PCI_NFUNC]; +static ddi_acc_handle_t dev_17_hdl[NB_PCI_NFUNC]; +static ddi_acc_handle_t dev_pci_hdl[NB_PCI_DEV]; + +void +nb_pci_cfg_setup(dev_info_t *dip) +{ + pci_regspec_t reg; + int i; + + reg.pci_phys_hi = 16 << PCI_REG_DEV_SHIFT; /* Bus=0, Dev=16, Func=0 */ + reg.pci_phys_mid = 0; + reg.pci_phys_low = 0; + reg.pci_size_hi = 0; + reg.pci_size_low = PCIE_CONF_HDR_SIZE; /* overriden in pciex */ + + for (i = 0; i < NB_PCI_NFUNC; i++) { + if (ddi_prop_update_int_array(DDI_MAJOR_T_UNKNOWN, dip, "reg", + (int *)®, sizeof (reg)/sizeof (int)) != DDI_PROP_SUCCESS) + cmn_err(CE_WARN, + "nb_pci_cfg_setup: cannot create reg property"); + + if (pci_config_setup(dip, &dev_16_hdl[i]) != DDI_SUCCESS) + cmn_err(CE_WARN, + "intel_nb5000: pci_config_setup failed"); + reg.pci_phys_hi += 1 << PCI_REG_FUNC_SHIFT; + } + reg.pci_phys_hi = 17 << PCI_REG_DEV_SHIFT; /* Bus=0, Dev=17, Func=0 */ + for (i = 0; i < NB_PCI_NFUNC; i++) { + if (ddi_prop_update_int_array(DDI_MAJOR_T_UNKNOWN, dip, "reg", + (int *)®, sizeof (reg)/sizeof (int)) != DDI_PROP_SUCCESS) + cmn_err(CE_WARN, + "nb_pci_cfg_setup: cannot create reg property"); + + if (pci_config_setup(dip, &dev_17_hdl[i]) != DDI_SUCCESS) + cmn_err(CE_WARN, + "intel_nb5000: pci_config_setup failed"); + reg.pci_phys_hi += 1 << PCI_REG_FUNC_SHIFT; + } + reg.pci_phys_hi = 0; /* Bus=0, Dev=0, Func=0 */ + for (i = 0; i < NB_PCI_DEV; i++) { + if (ddi_prop_update_int_array(DDI_MAJOR_T_UNKNOWN, dip, "reg", + (int *)®, sizeof (reg)/sizeof (int)) != DDI_PROP_SUCCESS) + cmn_err(CE_WARN, + "nb_pci_cfg_setup: cannot create reg property"); + + if (pci_config_setup(dip, &dev_pci_hdl[i]) != DDI_SUCCESS) + cmn_err(CE_WARN, + "intel_nb5000: pci_config_setup failed"); + reg.pci_phys_hi += 1 << PCI_REG_DEV_SHIFT; + } +} + +void +nb_pci_cfg_free() +{ + int i; + + for (i = 0; i < NB_PCI_NFUNC; i++) { + pci_config_teardown(&dev_16_hdl[i]); + } + for (i = 0; i < NB_PCI_NFUNC; i++) { + pci_config_teardown(&dev_17_hdl[i]); + } + for (i = 0; i < NB_PCI_DEV; i++) + pci_config_teardown(&dev_pci_hdl[i]); +} + +static ddi_acc_handle_t +nb_get_hdl(int bus, int dev, int func) +{ + ddi_acc_handle_t hdl; + + if (bus == 0 && dev == 16 && func < NB_PCI_NFUNC) { + hdl = dev_16_hdl[func]; + } else if (bus == 0 && dev == 17 && func < NB_PCI_NFUNC) { + hdl = dev_17_hdl[func]; + } else if (bus == 0 && dev < NB_PCI_DEV && func == 0) { + hdl = dev_pci_hdl[dev]; + } else { + hdl = 0; + } + return (hdl); +} + +uint8_t +nb_pci_getb(int bus, int dev, int func, int reg, int *interpose) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + return (cmi_pci_getb(bus, dev, func, reg, interpose, hdl)); +} + +uint16_t +nb_pci_getw(int bus, int dev, int func, int reg, int *interpose) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + return (cmi_pci_getw(bus, dev, func, reg, interpose, hdl)); +} + +uint32_t +nb_pci_getl(int bus, int dev, int func, int reg, int *interpose) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + return (cmi_pci_getl(bus, dev, func, reg, interpose, hdl)); +} + +void +nb_pci_putb(int bus, int dev, int func, int reg, uint8_t val) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + cmi_pci_putb(bus, dev, func, reg, hdl, val); +} + +void +nb_pci_putw(int bus, int dev, int func, int reg, uint16_t val) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + cmi_pci_putw(bus, dev, func, reg, hdl, val); +} + +void +nb_pci_putl(int bus, int dev, int func, int reg, uint32_t val) +{ + ddi_acc_handle_t hdl; + + hdl = nb_get_hdl(bus, dev, func); + cmi_pci_putl(bus, dev, func, reg, hdl, val); +} diff --git a/usr/src/uts/i86pc/io/intel_nb5000/rank.h b/usr/src/uts/i86pc/io/intel_nb5000/rank.h new file mode 100644 index 0000000000..c0206b67de --- /dev/null +++ b/usr/src/uts/i86pc/io/intel_nb5000/rank.h @@ -0,0 +1,53 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _RANK_H +#define _RANK_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +struct rank_base { + uint64_t base; + uint64_t limit; + uint32_t hole; + uint32_t hole_size; + int branch_interleave; + int way; + int interleave; + void *rank_geometry; +}; + +extern struct rank_base *rank_base; + +#ifdef __cplusplus +} +#endif + +#endif /* _RANK_H */ diff --git a/usr/src/uts/i86pc/io/mc/mcamd.h b/usr/src/uts/i86pc/io/mc/mcamd.h index 08c5b918dd..9294e464f3 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd.h +++ b/usr/src/uts/i86pc/io/mc/mcamd.h @@ -40,7 +40,9 @@ #include <sys/sunddi.h> #include <sys/ksynch.h> #include <sys/mc_amd.h> +#include <sys/cpu_module.h> #include <mcamd_api.h> +#include <mcamd_err.h> #include <mcamd_dimmcfg.h> #ifdef __cplusplus @@ -185,11 +187,14 @@ typedef struct mc_cfgregs { mcamd_cfgreg_t mcr_drammisc; mcamd_cfgreg_t mcr_nbcfg; mcamd_cfgreg_t mcr_sparectl; + mcamd_cfgreg_t mcr_scrubctl; + mcamd_cfgreg_t mcr_scrubaddrlo; + mcamd_cfgreg_t mcr_scrubaddrhi; } mc_cfgregs_t; struct mc { mc_hdr_t mc_hdr; /* id */ - struct mc *mc_next; /* linear, doubly-linked list */ + struct mc *mc_next; /* next MC instance */ const char *mc_revname; /* revision name string */ uint32_t mc_socket; /* Package type */ uint_t mc_ref; /* reference (attach) count */ @@ -217,9 +222,22 @@ extern mc_t *mc_list; extern krwlock_t mc_lock; extern void mcamd_mkhdl(mcamd_hdl_t *); -extern void mcamd_mc_register(struct cpu *); +extern void mcamd_mc_register(cmi_hdl_t, mc_t *); extern void mcamd_ereport_post(mc_t *, const char *, mc_unum_t *, uint64_t); +/* + * mcamd_mc_ops prototypes + */ +extern cmi_errno_t mcamd_patounum_wrap(void *, uint64_t, uint8_t, uint8_t, + uint32_t, int, mc_unum_t *); +extern cmi_errno_t mcamd_unumtopa_wrap(void *, mc_unum_t *, nvlist_t *, + uint64_t *); + +/* + * Internal functions + */ +cmi_errno_t mcamd_cmierr(int, mcamd_hdl_t *); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c index 20623dfee9..4a2d59ce2e 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c +++ b/usr/src/uts/i86pc/io/mc/mcamd_dimmcfg.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -335,13 +335,13 @@ static const struct { const struct mcdcfg_csmapline *map; int nmapents; } csmap_tbls[DCFG_NTBL] = { - { MC_REVS_BCDE, 0, &csmap_nomod64mux_preF[0], + { MC_F_REVS_BCDE, 0, &csmap_nomod64mux_preF[0], sizeof (csmap_nomod64mux_preF) / sizeof (struct mcdcfg_csmapline) }, - { MC_REVS_BCDE, 1, &csmap_mod64mux_preF[0], + { MC_F_REVS_BCDE, 1, &csmap_mod64mux_preF[0], sizeof (csmap_mod64mux_preF) / sizeof (struct mcdcfg_csmapline) }, - { MC_REVS_FG, 0, &csmap_nomod64mux_fg[0], + { MC_F_REVS_FG, 0, &csmap_nomod64mux_fg[0], sizeof (csmap_nomod64mux_fg) / sizeof (struct mcdcfg_csmapline) }, - { MC_REVS_FG, 1, &csmap_mod64mux_fg[0], + { MC_F_REVS_FG, 1, &csmap_mod64mux_fg[0], sizeof (csmap_mod64mux_fg) / sizeof (struct mcdcfg_csmapline) } }; diff --git a/usr/src/uts/i86pc/io/mc/mcamd_drv.c b/usr/src/uts/i86pc/io/mc/mcamd_drv.c index 3490c08e2a..d7c9371795 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd_drv.c +++ b/usr/src/uts/i86pc/io/mc/mcamd_drv.c @@ -56,6 +56,11 @@ #include <sys/fm/cpu/AMD.h> /* + * Set to prevent mc-amd from attaching. + */ +int mc_no_attach = 0; + +/* * Of the 754/939/940 packages, only socket 940 supports quadrank registered * dimms. Unfortunately, no memory-controller register indicates the * presence of quadrank dimm support or presence (i.e., in terms of number @@ -73,6 +78,23 @@ int mc_hold_attached = 1; #define MAX(m, n) ((m) >= (n) ? (m) : (n)) #define MIN(m, n) ((m) <= (n) ? (m) : (n)) +/* + * The following tuneable is used to determine the DRAM scrubbing rate. + * The values range from 0x00-0x16 as described in the BKDG. Zero + * disables DRAM scrubbing. Values above zero indicate rates in descending + * order. + * + * The default value below is used on several Sun systems. In the future + * this code should assign values dynamically based on memory sizing. + */ +uint32_t mc_scrub_rate_dram = 0xd; /* 64B every 163.8 us; 1GB per 45 min */ + +enum { + MC_SCRUB_BIOSDEFAULT, /* retain system default value */ + MC_SCRUB_FIXED, /* assign mc_scrub_rate_* values */ + MC_SCRUB_MAX /* assign max of system and tunables */ +} mc_scrub_policy = MC_SCRUB_MAX; + static void mc_snapshot_destroy(mc_t *mc) { @@ -183,8 +205,9 @@ mc_ecc_enabled(mc_t *mc) MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg; - return (MC_REV_MATCH(rev, MC_REVS_BCDE) ? - MCREG_FIELD_preF(&nbcfg, EccEn) : MCREG_FIELD_revFG(&nbcfg, EccEn)); + return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ? + MCREG_FIELD_F_preF(&nbcfg, EccEn) : + MCREG_FIELD_F_revFG(&nbcfg, EccEn)); } static uint32_t @@ -195,9 +218,9 @@ mc_ck_enabled(mc_t *mc) MCREG_VAL32(&nbcfg) = mc->mc_cfgregs.mcr_nbcfg; - return (MC_REV_MATCH(rev, MC_REVS_BCDE) ? - MCREG_FIELD_preF(&nbcfg, ChipKillEccEn) : - MCREG_FIELD_revFG(&nbcfg, ChipKillEccEn)); + return (MC_REV_MATCH(rev, MC_F_REVS_BCDE) ? + MCREG_FIELD_F_preF(&nbcfg, ChipKillEccEn) : + MCREG_FIELD_F_revFG(&nbcfg, ChipKillEccEn)); } static void @@ -525,15 +548,15 @@ mc_dimmlist_create(mc_t *mc) if (mc->mc_socket == X86_SOCKET_940) r4 = mc_quadranksupport != 0; else if (mc->mc_socket == X86_SOCKET_F1207) - r4 = MCREG_FIELD_revFG(drcfghip, FourRankRDimm); + r4 = MCREG_FIELD_F_revFG(drcfghip, FourRankRDimm); /* * Are we dealing with quadrank SO-DIMMs? These are supported * in AM2 and S1g1 packages only, but in all rev F/G cases we * can detect their presence via a bit in the dram config high reg. */ - if (MC_REV_MATCH(rev, MC_REVS_FG)) - s4 = MCREG_FIELD_revFG(drcfghip, FourRankSODimm); + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) + s4 = MCREG_FIELD_F_revFG(drcfghip, FourRankSODimm); for (mccs = mc->mc_cslist; mccs != NULL; mccs = mccs->mccs_next) { mcdcfg_rslt_t rslt; @@ -597,6 +620,7 @@ mc_report_testfails(mc_t *mc) unum.unum_board = 0; unum.unum_chip = mc->mc_props.mcp_num; unum.unum_mc = 0; + unum.unum_chan = MC_INVALNUM; unum.unum_cs = mccs->mccs_props.csp_num; unum.unum_rank = mccs->mccs_props.csp_dimmrank; unum.unum_offset = MCAMD_RC_INVALID_OFFSET; @@ -646,48 +670,31 @@ mc_mkprops_htcfg(mc_pcicfg_hdl_t cfghdl, mc_t *mc) static void mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc) { - union mcreg_drambase base[MC_CHIP_MAXNODES]; - union mcreg_dramlimit lim[MC_CHIP_MAXNODES]; + union mcreg_drambase basereg; + union mcreg_dramlimit limreg; mc_props_t *mcp = &mc->mc_props; mc_cfgregs_t *mcr = &mc->mc_cfgregs; union mcreg_dramhole hole; - int i; - - mc_prop_read_pair(cfghdl, - (uint32_t *)base, MC_AM_REG_DRAMBASE_0, MC_CHIP_MAXNODES, - (uint32_t *)lim, MC_AM_REG_DRAMLIM_0, MC_CHIP_MAXNODES, - MC_AM_REG_DRAM_INCR); - - for (i = 0; i < MC_CHIP_MAXNODES; i++) { - /* - * Don't create properties for empty nodes. - */ - if (MCREG_FIELD_CMN(&lim[i], DRAMLimiti) == 0) - continue; + int nodeid = mc->mc_props.mcp_num; - /* - * Skip all nodes but the one that matches the chip id - * for the mc currently being attached. Since the chip id - * is the HT id this loop and the preceding read of all - * base/limit pairs is overkill. - */ - if (MCREG_FIELD_CMN(&lim[i], DstNode) != mc->mc_props.mcp_num) - continue; - - /* - * Stash raw register values for this node - */ - mcr->mcr_drambase = MCREG_VAL32(&base[i]); - mcr->mcr_dramlimit = MCREG_VAL32(&lim[i]); + mcr->mcr_drambase = MCREG_VAL32(&basereg) = mc_pcicfg_get32(cfghdl, + MC_AM_REG_DRAMBASE_0 + nodeid * MC_AM_REG_DRAM_INCR); - /* - * Derive some "cooked" properties - */ - mcp->mcp_base = MC_DRAMBASE(&base[i]); - mcp->mcp_lim = MC_DRAMLIM(&lim[i]); - mcp->mcp_ilen = MCREG_FIELD_CMN(&base[i], IntlvEn); - mcp->mcp_ilsel = MCREG_FIELD_CMN(&lim[i], IntlvSel); + mcr->mcr_dramlimit = MCREG_VAL32(&limreg) = mc_pcicfg_get32(cfghdl, + MC_AM_REG_DRAMLIM_0 + nodeid * MC_AM_REG_DRAM_INCR); + /* + * Derive some "cooked" properties for nodes that have a range of + * physical addresses that are read or write enabled and for which + * the DstNode matches the node we are attaching. + */ + if (MCREG_FIELD_CMN(&limreg, DRAMLimiti) != 0 && + MCREG_FIELD_CMN(&limreg, DstNode) == nodeid && + (MCREG_FIELD_CMN(&basereg, WE) || MCREG_FIELD_CMN(&basereg, RE))) { + mcp->mcp_base = MC_DRAMBASE(&basereg); + mcp->mcp_lim = MC_DRAMLIM(&limreg); + mcp->mcp_ilen = MCREG_FIELD_CMN(&basereg, IntlvEn); + mcp->mcp_ilsel = MCREG_FIELD_CMN(&limreg, IntlvSel); } /* @@ -696,14 +703,12 @@ mc_mkprops_addrmap(mc_pcicfg_hdl_t cfghdl, mc_t *mc) * hole base and offset for this node. This was introduced in * revision E. */ - if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_REV_E)) { - MCREG_VAL32(&hole) = + if (MC_REV_ATLEAST(mc->mc_props.mcp_rev, MC_F_REV_E)) { + mcr->mcr_dramhole = MCREG_VAL32(&hole) = mc_pcicfg_get32(cfghdl, MC_AM_REG_HOLEADDR); - mcr->mcr_dramhole = MCREG_VAL32(&hole); - if (MCREG_FIELD_CMN(&hole, DramHoleValid)) { + if (MCREG_FIELD_CMN(&hole, DramHoleValid)) mcp->mcp_dramhole_size = MC_DRAMHOLE_SIZE(&hole); - } } } @@ -721,14 +726,14 @@ mc_getmiscctl(mc_t *mc) mc->mc_cfgregs.mcr_nbcfg = MCREG_VAL32(&nbcfg) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_NBCFG); - if (MC_REV_MATCH(rev, MC_REVS_FG)) { + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { mc->mc_cfgregs.mcr_sparectl = MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); - if (MCREG_FIELD_revFG(&sparectl, SwapDone)) { + if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) { mc->mc_props.mcp_badcs = - MCREG_FIELD_revFG(&sparectl, BadDramCs); + MCREG_FIELD_F_revFG(&sparectl, BadDramCs); } } } @@ -796,10 +801,10 @@ mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) * Note the DRAM controller width. The 64/128 bit is in a different * bit position for revision F and G. */ - if (MC_REV_MATCH(rev, MC_REVS_FG)) { - wide = MCREG_FIELD_revFG(&drcfg_lo, Width128); + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { + wide = MCREG_FIELD_F_revFG(&drcfg_lo, Width128); } else { - wide = MCREG_FIELD_preF(&drcfg_lo, Width128); + wide = MCREG_FIELD_F_preF(&drcfg_lo, Width128); } mcp->mcp_accwidth = wide ? 128 : 64; @@ -808,12 +813,12 @@ mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) * revs that support it. This include the Mod64Mux indication on * these revs - for rev E it is in DRAM config low. */ - if (MC_REV_MATCH(rev, MC_REVS_FG)) { + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { mcr->mcr_drammisc = MCREG_VAL32(&drmisc) = mc_pcicfg_get32(cfghdl, MC_DC_REG_DRAMMISC); - mcp->mcp_mod64mux = MCREG_FIELD_revFG(&drmisc, Mod64Mux); - } else if (MC_REV_MATCH(rev, MC_REV_E)) { - mcp->mcp_mod64mux = MCREG_FIELD_preF(&drcfg_lo, Mod64BitMux); + mcp->mcp_mod64mux = MCREG_FIELD_F_revFG(&drmisc, Mod64Mux); + } else if (MC_REV_MATCH(rev, MC_F_REV_E)) { + mcp->mcp_mod64mux = MCREG_FIELD_F_preF(&drcfg_lo, Mod64BitMux); } /* @@ -830,10 +835,11 @@ mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) * introduced as an option in rev E, but the bit that indicates it * is enabled has moved in revs F/G. */ - if (MC_REV_MATCH(rev, MC_REV_E)) { - mcp->mcp_bnkswzl = MCREG_FIELD_preF(&baddrmap, BankSwizzleMode); - } else if (MC_REV_MATCH(rev, MC_REVS_FG)) { - mcp->mcp_bnkswzl = MCREG_FIELD_revFG(&drcfg_hi, + if (MC_REV_MATCH(rev, MC_F_REV_E)) { + mcp->mcp_bnkswzl = + MCREG_FIELD_F_preF(&baddrmap, BankSwizzleMode); + } else if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { + mcp->mcp_bnkswzl = MCREG_FIELD_F_revFG(&drcfg_hi, BankSwizzleMode); } @@ -842,7 +848,7 @@ mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) * to F have an equal number of base and mask registers; revision F * has twice as many base registers as masks. */ - maskdivisor = MC_REV_MATCH(rev, MC_REVS_FG) ? 2 : 1; + maskdivisor = MC_REV_MATCH(rev, MC_F_REVS_FG) ? 2 : 1; mc_prop_read_pair(cfghdl, (uint32_t *)base, MC_DC_REG_CSBASE_0, MC_CHIP_NCS, @@ -860,12 +866,12 @@ mc_mkprops_dramctl(mc_pcicfg_hdl_t cfghdl, mc_t *mc) size_t sz; int csbe, spare, testfail; - if (MC_REV_MATCH(rev, MC_REVS_FG)) { - csbe = MCREG_FIELD_revFG(&base[i], CSEnable); - spare = MCREG_FIELD_revFG(&base[i], Spare); - testfail = MCREG_FIELD_revFG(&base[i], TestFail); + if (MC_REV_MATCH(rev, MC_F_REVS_FG)) { + csbe = MCREG_FIELD_F_revFG(&base[i], CSEnable); + spare = MCREG_FIELD_F_revFG(&base[i], Spare); + testfail = MCREG_FIELD_F_revFG(&base[i], TestFail); } else { - csbe = MCREG_FIELD_preF(&base[i], CSEnable); + csbe = MCREG_FIELD_F_preF(&base[i], CSEnable); spare = 0; testfail = 0; } @@ -1043,7 +1049,7 @@ mc_onlinespare(mc_t *mc, int csnum) ASSERT(RW_WRITE_HELD(&mc_lock)); - if (!MC_REV_MATCH(mcp->mcp_rev, MC_REVS_FG)) + if (!MC_REV_MATCH(mcp->mcp_rev, MC_F_REVS_FG)) return (ENOTSUP); /* MC rev does not offer online spare */ else if (mcp->mcp_sparecs == MC_INVALNUM) return (ENODEV); /* Supported, but no spare configured */ @@ -1074,16 +1080,16 @@ mc_onlinespare(mc_t *mc, int csnum) MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); - if (MCREG_FIELD_revFG(&sparectl, SwapDone)) + if (MCREG_FIELD_F_revFG(&sparectl, SwapDone)) return (EBUSY); /* Write to the BadDramCs field */ - MCREG_FIELD_revFG(&sparectl, BadDramCs) = csnum; + MCREG_FIELD_F_revFG(&sparectl, BadDramCs) = csnum; mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); /* And request that the swap to the spare start */ - MCREG_FIELD_revFG(&sparectl, SwapEn) = 1; + MCREG_FIELD_F_revFG(&sparectl, SwapEn) = 1; mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL, MCREG_VAL32(&sparectl)); @@ -1111,10 +1117,10 @@ mc_onlinespare(mc_t *mc, int csnum) MCREG_VAL32(&sparectl) = mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SPARECTL); - } while (!MCREG_FIELD_revFG(&sparectl, SwapDone) && + } while (!MCREG_FIELD_F_revFG(&sparectl, SwapDone) && gethrtime() < tmax); - if (!MCREG_FIELD_revFG(&sparectl, SwapDone)) + if (!MCREG_FIELD_F_revFG(&sparectl, SwapDone)) return (ETIME); /* Operation timed out */ mcp->mcp_badcs = csnum; @@ -1261,55 +1267,50 @@ mc_fm_init(dev_info_t *dip) ddi_fm_handler_register(dip, mc_fm_handle, NULL); } -static void -mc_fm_fini(dev_info_t *dip) +/*ARGSUSED*/ +static int +mc_create_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) { - pci_ereport_teardown(dip); - ddi_fm_fini(dip); + chipid_t chipid = *((chipid_t *)arg1); + cmi_hdl_t *hdlp = (cmi_hdl_t *)arg2; + + if (cmi_hdl_chipid(whdl) == chipid) { + cmi_hdl_hold(whdl); /* short-term hold */ + *hdlp = whdl; + return (CMI_HDL_WALK_DONE); + } else { + return (CMI_HDL_WALK_NEXT); + } } static mc_t * mc_create(chipid_t chipid) { mc_t *mc; - cpu_t *cpu; + cmi_hdl_t hdl = NULL; ASSERT(RW_WRITE_HELD(&mc_lock)); - mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP); - /* - * Find one of a chip's CPU. + * Find a handle for one of a chip's CPU. * * We can use one of the chip's CPUs since all cores * of a chip share the same revision and socket type. */ - kpreempt_disable(); - cpu = cpu_list; - do { - if (cpuid_get_chipid(cpu) == chipid) - break; - } while ((cpu = cpu->cpu_next) != cpu_list); + cmi_hdl_walk(mc_create_cb, (void *)&chipid, (void *)&hdl, NULL); + if (hdl == NULL) + return (NULL); /* no cpu for this chipid found! */ - if (cpuid_get_chipid(cpu) != chipid) { - /* - * Couldn't find a cpu with the specified chipid - */ - kpreempt_enable(); - kmem_free(mc, sizeof (mc_t)); - return (NULL); - } + mc = kmem_zalloc(sizeof (mc_t), KM_SLEEP); mc->mc_hdr.mch_type = MC_NT_MC; mc->mc_props.mcp_num = chipid; mc->mc_props.mcp_sparecs = MC_INVALNUM; mc->mc_props.mcp_badcs = MC_INVALNUM; - mc->mc_props.mcp_rev = cpuid_getchiprev(cpu); - mc->mc_revname = cpuid_getchiprevstr(cpu); - mc->mc_socket = cpuid_getsockettype(cpu); - - kpreempt_enable(); + mc->mc_props.mcp_rev = cmi_hdl_chiprev(hdl); + mc->mc_revname = cmi_hdl_chiprevstr(hdl); + mc->mc_socket = cmi_hdl_getsockettype(hdl); if (mc_list == NULL) mc_list = mc; @@ -1319,9 +1320,163 @@ mc_create(chipid_t chipid) mc->mc_next = NULL; mc_last = mc; + cmi_hdl_rele(hdl); + return (mc); } +/* + * 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 +mc_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 memory scrubber. We must use the mc_pcicfg_{get32,put32}_nohdl + * interfaces since we do not bind to function 3. + */ +cmi_errno_t +mc_scrubber_enable(mc_t *mc) +{ + mc_props_t *mcp = &mc->mc_props; + chipid_t chipid = (chipid_t)mcp->mcp_num; + uint32_t rev = (uint32_t)mcp->mcp_rev; + mc_cfgregs_t *mcr = &mc->mc_cfgregs; + union mcreg_scrubctl scrubctl; + union mcreg_dramscrublo dalo; + union mcreg_dramscrubhi dahi; + + mcr->mcr_scrubctl = MCREG_VAL32(&scrubctl) = + mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL); + + mcr->mcr_scrubaddrlo = MCREG_VAL32(&dalo) = + mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO); + + mcr->mcr_scrubaddrhi = MCREG_VAL32(&dahi) = + mc_pcicfg_get32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI); + + if (mc_scrub_policy == MC_SCRUB_BIOSDEFAULT) + return (MCREG_FIELD_CMN(&scrubctl, DramScrub) != + AMD_NB_SCRUBCTL_RATE_NONE ? + CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB); + + /* + * Disable DRAM scrubbing while we fiddle. + */ + MCREG_FIELD_CMN(&scrubctl, DramScrub) = AMD_NB_SCRUBCTL_RATE_NONE; + mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, + MCREG_VAL32(&scrubctl)); + + /* + * Setup DRAM Scrub Address Low and High registers for the + * base address of this node, and to select srubber redirect. + */ + MCREG_FIELD_CMN(&dalo, ScrubReDirEn) = 1; + MCREG_FIELD_CMN(&dalo, ScrubAddrLo) = + AMD_NB_SCRUBADDR_MKLO(mcp->mcp_base); + + MCREG_FIELD_CMN(&dahi, ScrubAddrHi) = + AMD_NB_SCRUBADDR_MKHI(mcp->mcp_base); + + mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_LO, + MCREG_VAL32(&dalo)); + mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBADDR_HI, + MCREG_VAL32(&dahi)); + + if (mc_scrub_rate_dram > AMD_NB_SCRUBCTL_RATE_MAX) { + cmn_err(CE_WARN, "mc_scrub_rate_dram is too large; " + "resetting to 0x%x\n", AMD_NB_SCRUBCTL_RATE_MAX); + mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_MAX; + } + + switch (mc_scrub_policy) { + case MC_SCRUB_FIXED: + /* Use the system value checked above */ + break; + + default: + cmn_err(CE_WARN, "Unknown mc_scrub_policy value %d - " + "using default policy of MC_SCRUB_MAX", mc_scrub_policy); + /*FALLTHRU*/ + + case MC_SCRUB_MAX: + mc_scrub_rate_dram = mc_scrubber_max(mc_scrub_rate_dram, + mcr->mcr_scrubctl, AMD_NB_SCRUBCTL_DRAM_MASK, + AMD_NB_SCRUBCTL_DRAM_SHIFT); + break; + } + +#ifdef OPTERON_ERRATUM_99 + /* + * This erratum applies on revisions D and earlier. + * + * Do not enable the dram scrubber is the chip-select ranges + * for the node are not contiguous. + */ + if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE && + mc->mc_csdiscontig && + !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { + cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " + "%s chip %d because DRAM hole is present on this node", + mc->mc_revname, chipid); + mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE; + } +#endif + +#ifdef OPTERON_ERRATUM_101 + /* + * This erratum applies on revisions D and earlier. + * + * 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 (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE && + mcp->mcp_ilen != 0 && + !X86_CHIPREV_ATLEAST(rev, X86_CHIPREV_AMD_F_REV_E)) { + cmn_err(CE_CONT, "?Opteron DRAM scrubber disabled on revision " + "%s chip %d because DRAM memory is node-interleaved", + mc->mc_revname, chipid); + mc_scrub_rate_dram = AMD_NB_SCRUBCTL_RATE_NONE; + } +#endif + + if (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE) { + MCREG_FIELD_CMN(&scrubctl, DramScrub) = mc_scrub_rate_dram; + mc_pcicfg_put32_nohdl(mc, MC_FUNC_MISCCTL, MC_CTL_REG_SCRUBCTL, + MCREG_VAL32(&scrubctl)); + } + + return (mc_scrub_rate_dram != AMD_NB_SCRUBCTL_RATE_NONE ? + CMI_SUCCESS : CMIERR_MC_NOMEMSCRUB); +} + +/*ARGSUSED*/ +static int +mc_attach_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) +{ + mc_t *mc = (mc_t *)arg1; + mcamd_prop_t chipid = *((mcamd_prop_t *)arg2); + + if (cmi_hdl_chipid(whdl) == chipid) { + mcamd_mc_register(whdl, mc); + } + + return (CMI_HDL_WALK_NEXT); +} + static int mc_sw_scrub_disabled = 0; static int @@ -1334,10 +1489,9 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) enum mc_funcnum func; long unitaddr; int chipid, rc; - cpu_t *cpu; mc_t *mc; - if (cmd != DDI_ATTACH) + if (cmd != DDI_ATTACH || mc_no_attach != 0) return (DDI_FAILURE); bindnm = ddi_binding_name(dip); @@ -1411,8 +1565,6 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 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. @@ -1437,16 +1589,13 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) if (func == MC_FUNC_DEVIMAP) { mc_props_t *mcp = &mc->mc_props; int dram_present = 0; - pg_cpu_itr_t itr; - pghw_t *chp; - cpu_t *cpup; if (ddi_create_minor_node(dip, "mc-amd", S_IFCHR, - mc->mc_props.mcp_num, "ddi_mem_ctrl", + mcp->mcp_num, "ddi_mem_ctrl", 0) != DDI_SUCCESS) { cmn_err(CE_WARN, "failed to create minor node for chip " "%d memory controller\n", - (chipid_t)mc->mc_props.mcp_num); + (chipid_t)mcp->mcp_num); } /* @@ -1454,40 +1603,13 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) * * If there is memory present on this node and ECC is enabled * attempt to enable h/w memory scrubbers for this node. - * Note that if the generic cpu module has loaded then this - * will have no effect and cmi_scrubber_enable will return - * 0. If we are successful in enabling the hardware scrubbers, + * If we are successful in enabling *any* hardware scrubbers, * disable the software memory scrubber. */ - kpreempt_disable(); /* prevent cpu list from changing */ + cmi_hdl_walk(mc_attach_cb, (void *)mc, (void *)&mcp->mcp_num, + NULL); - chp = pghw_find_by_instance(mc->mc_props.mcp_num, PGHW_CHIP); - if (chp == NULL) { - /* - * No chip grouping was found (single core processors). - * Find the CPU to register by searching the CPU list. - */ - cpu = cpu_list; - do { - if (cpuid_get_chipid(cpu) == - mc->mc_props.mcp_num) - break; - } while ((cpu = cpu->cpu_next) != cpu_list); - ASSERT(cpuid_get_chipid(cpu) == mc->mc_props.mcp_num); - - mcamd_mc_register(cpu); - } else { - /* - * Iterate over / register the chip's CPUs - */ - PG_CPU_ITR_INIT(chp, itr); - cpup = cpu = pg_cpu_next(&itr); - do { - mcamd_mc_register(cpup); - } while ((cpup = pg_cpu_next(&itr)) != NULL); - } - - if (mc->mc_props.mcp_lim != mc->mc_props.mcp_base) { + if (mcp->mcp_lim != mcp->mcp_base) { /* * This node may map non-dram memory alone, so we * must check for an enabled chip-select to be @@ -1520,14 +1642,17 @@ mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) /* No memory on this node - others decide memscrub */ rc = 0; } else { - /* There is memory on this node and ECC is enabled */ - rc = cmi_scrubber_enable(cpu, mcp->mcp_base, - mcp->mcp_ilen, mc->mc_csdiscontig); + /* + * There is memory on this node and ECC is enabled. + * Call via the cpu module to enable memory scrubbing + * on this node - we could call directly but then + * we may overlap with a request to enable chip-cache + * scrubbing. + */ + rc = mc_scrubber_enable(mc); } - kpreempt_enable(); - - if (rc && !mc_sw_scrub_disabled++) + if (rc == CMI_SUCCESS && !mc_sw_scrub_disabled++) memscrub_disable(); mc_report_testfails(mc); diff --git a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c index 4445a23ae2..52013eb972 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c +++ b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.c @@ -30,7 +30,7 @@ #include <sys/sunddi.h> #include <mcamd_pcicfg.h> -#include <sys/pci_cfgspace_impl.h> +#include <sys/pci_cfgspace.h> struct _mc_pcicfg_hdl { mc_t *cfh_mc; @@ -44,6 +44,9 @@ mccfgsetup(struct _mc_pcicfg_hdl *hdlp, mc_t *mc, enum mc_funcnum func) hdlp->cfh_mc = mc; hdlp->cfh_func = func; + if (mc->mc_funcs[func].mcf_devi == NULL) + return (DDI_FAILURE); + if (pci_config_setup(mc->mc_funcs[func].mcf_devi, &hdlp->cfh_hdl) != DDI_SUCCESS) return (DDI_FAILURE); @@ -83,19 +86,25 @@ mc_pcicfg_get32(mc_pcicfg_hdl_t cookie, off_t offset) return (pci_config_get32(hdlp->cfh_hdl, offset)); } +void +mc_pcicfg_put32(mc_pcicfg_hdl_t cookie, off_t offset, uint32_t val) +{ + struct _mc_pcicfg_hdl *hdlp = cookie; + + pci_config_put32(hdlp->cfh_hdl, offset, val); +} + uint32_t mc_pcicfg_get32_nohdl(mc_t *mc, enum mc_funcnum func, off_t offset) { - return (pci_mech1_getl(0, - MC_AMD_DEV_OFFSET + mc->mc_props.mcp_num, - func, offset)); + return ((*pci_getl_func)(0, MC_AMD_DEV_OFFSET + mc->mc_props.mcp_num, + func, offset)); } void mc_pcicfg_put32_nohdl(mc_t *mc, enum mc_funcnum func, off_t offset, uint32_t val) { - pci_mech1_putl(0, - MC_AMD_DEV_OFFSET + mc->mc_props.mcp_num, + (*pci_putl_func)(0, MC_AMD_DEV_OFFSET + mc->mc_props.mcp_num, func, offset, val); } diff --git a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h index 65ab6cdb8e..f5d6fef7ac 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h +++ b/usr/src/uts/i86pc/io/mc/mcamd_pcicfg.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -40,10 +40,13 @@ extern "C" { typedef void * mc_pcicfg_hdl_t; -/* MC PCI config where we have attached to an MC dev/function */ +/* + * MC PCI config where we have attached to an MC dev/function. + */ extern int mc_pcicfg_setup(mc_t *, enum mc_funcnum, mc_pcicfg_hdl_t *); extern void mc_pcicfg_teardown(mc_pcicfg_hdl_t); extern uint32_t mc_pcicfg_get32(mc_pcicfg_hdl_t, off_t); +extern void mc_pcicfg_put32(mc_pcicfg_hdl_t cookie, off_t offset, uint32_t val); /* MC PCI config where we have not attached to the dev/function */ extern uint32_t mc_pcicfg_get32_nohdl(mc_t *, enum mc_funcnum, off_t); diff --git a/usr/src/uts/i86pc/io/mc/mcamd_subr.c b/usr/src/uts/i86pc/io/mc/mcamd_subr.c index f072e37b1a..432968c1c6 100644 --- a/usr/src/uts/i86pc/io/mc/mcamd_subr.c +++ b/usr/src/uts/i86pc/io/mc/mcamd_subr.c @@ -425,8 +425,33 @@ mcamd_mkhdl(mcamd_hdl_t *hdl) hdl->mcamd_debug = mcamd_debug; } +cmi_errno_t +mcamd_cmierr(int err, mcamd_hdl_t *hdl) +{ + if (err == 0) + return (CMI_SUCCESS); + + switch (mcamd_errno(hdl)) { + case EMCAMD_SYNDINVALID: + return (CMIERR_MC_SYNDROME); + + case EMCAMD_TREEINVALID: + return (CMIERR_MC_BADSTATE); + + case EMCAMD_NOADDR: + return (CMIERR_MC_NOADDR); + + case EMCAMD_INSUFF_RES: + return (CMIERR_MC_ADDRBITS); + + default: + return (CMIERR_UNKNOWN); + } + +} + /*ARGSUSED*/ -static int +cmi_errno_t mcamd_patounum_wrap(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, int syndtype, mc_unum_t *unump) { @@ -457,7 +482,7 @@ mcamd_patounum_wrap(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, #endif rw_exit(&mc_lock); - return (rc == 0); + return (mcamd_cmierr(rc, &mcamd)); } static int @@ -475,6 +500,7 @@ fmri2unum(nvlist_t *nvl, mc_unum_t *unump) bzero(unump, sizeof (mc_unum_t)); + unump->unum_chan = MC_INVALNUM; for (i = 0; i < MC_UNUM_NDIMM; i++) unump->unum_dimms[i] = MC_INVALNUM; @@ -507,19 +533,18 @@ fmri2unum(nvlist_t *nvl, mc_unum_t *unump) } /*ARGSUSED*/ -static int +cmi_errno_t 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); + ASSERT(unump == NULL || nvl == NULL); /* enforced at cmi level */ if (unump == NULL) { if (!fmri2unum(nvl, &unum)) - return (0); + return (CMIERR_MC_INVALUNUM); unump = &unum; } @@ -529,18 +554,7 @@ mcamd_unumtopa_wrap(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap) 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); + return (mcamd_cmierr(rc, &mcamd)); } static void @@ -660,3 +674,15 @@ mcamd_ereport_post(mc_t *mc, const char *class_sfx, mc_unum_t *unump, (void) fm_ereport_post(ereport, EVCH_TRYHARD); fm_nvlist_destroy(ereport, FM_NVA_FREE); } + +static const cmi_mc_ops_t mcamd_mc_ops = { + mcamd_patounum_wrap, /* cmi_mc_patounum */ + mcamd_unumtopa_wrap, /* cmi_mc_unumtopa */ + NULL /* cmi_mc_logout */ +}; + +void +mcamd_mc_register(cmi_hdl_t hdl, mc_t *mc) +{ + cmi_mc_register(hdl, &mcamd_mc_ops, mc); +} diff --git a/usr/src/uts/i86pc/mc-amd/Makefile b/usr/src/uts/i86pc/mc-amd/Makefile index 9fbfc831d0..576099acd9 100644 --- a/usr/src/uts/i86pc/mc-amd/Makefile +++ b/usr/src/uts/i86pc/mc-amd/Makefile @@ -17,7 +17,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -72,14 +72,6 @@ $(MCAMD_OFF_H): $(MCAMD_OFF_SRC) $(OFFSETS_CREATE) <$(MCAMD_OFF_SRC) >$@ # -# For now, disable these lint checks; maintainers should endeavor -# to investigate and remove these for maximum lint coverage. -# Please do not carry these forward to new Makefiles. -# -LINTTAGS += -erroff=E_STATIC_UNUSED -LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV - -# # Default build targets. # .KEEP_STATE: diff --git a/usr/src/uts/i86pc/os/cmi.c b/usr/src/uts/i86pc/os/cmi.c index 0e5129ebdc..ed96e3a205 100644 --- a/usr/src/uts/i86pc/os/cmi.c +++ b/usr/src/uts/i86pc/os/cmi.c @@ -30,23 +30,32 @@ * Public interface to routines implemented by CPU modules */ +#include <sys/types.h> +#include <sys/atomic.h> #include <sys/x86_archext.h> #include <sys/cpu_module_impl.h> +#include <sys/cpu_module_ms.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> +#include <sys/fm/protocol.h> +#include <sys/pcb.h> +#include <sys/ontrap.h> +#include <sys/psw.h> +#include <sys/privregs.h> -#define CPUMOD_SUBDIR "cpu" -#define CPUMOD_PREFIX "cpu" +/* + * Set to force cmi_init to fail. + */ +int cmi_no_init = 0; -#define CMI_OPS(cpu) \ - (cpu)->cpu_m.mcpu_cmi->cmi_ops -#define CMI_DATA(cpu) \ - (cpu)->cpu_m.mcpu_cmidata +/* + * Set to avoid MCA initialization. + */ +int cmi_no_mca_init = 0; /* * If cleared for debugging we will not attempt to load a model-specific @@ -61,23 +70,138 @@ int cmi_force_generic = 0; */ int cmi_panic_on_uncorrectable_error = 1; +/* + * Subdirectory (relative to the module search path) in which we will + * look for cpu modules. + */ +#define CPUMOD_SUBDIR "cpu" + +/* + * CPU modules have a filenames such as "cpu.AuthenticAMD.15" and + * "cpu.generic" - the "cpu" prefix is specified by the following. + */ +#define CPUMOD_PREFIX "cpu" + +/* + * Structure used to keep track of cpu modules we have loaded and their ops + */ +typedef struct cmi { + struct cmi *cmi_next; + struct cmi *cmi_prev; + const cmi_ops_t *cmi_ops; + struct modctl *cmi_modp; + uint_t cmi_refcnt; +} cmi_t; + static cmi_t *cmi_list; static kmutex_t cmi_load_lock; -static int -cmi_cpu_match(cpu_t *c1, cpu_t *c2) +/* + * Functions we need from cmi_hw.c that are not part of the cpu_module.h + * interface. + */ +extern cmi_hdl_t cmi_hdl_create(enum cmi_hdl_class, uint_t, uint_t, uint_t); +extern void cmi_hdl_setcmi(cmi_hdl_t, void *, void *); +extern void *cmi_hdl_getcmi(cmi_hdl_t); +extern void cmi_hdl_setmc(cmi_hdl_t, const struct cmi_mc_ops *, void *); + +#define HDL2CMI(hdl) cmi_hdl_getcmi(hdl) + +#define CMI_OPS(cmi) (cmi)->cmi_ops +#define CMI_OP_PRESENT(cmi, op) ((cmi) && CMI_OPS(cmi)->op != NULL) + +#define CMI_MATCH_VENDOR 0 /* Just match on vendor */ +#define CMI_MATCH_FAMILY 1 /* Match down to family */ +#define CMI_MATCH_MODEL 2 /* Match down to model */ +#define CMI_MATCH_STEPPING 3 /* Match down to stepping */ + +static void +cmi_link(cmi_t *cmi) +{ + ASSERT(MUTEX_HELD(&cmi_load_lock)); + + cmi->cmi_prev = NULL; + cmi->cmi_next = cmi_list; + if (cmi_list != NULL) + cmi_list->cmi_prev = cmi; + cmi_list = cmi; +} + +static void +cmi_unlink(cmi_t *cmi) { - 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); + ASSERT(MUTEX_HELD(&cmi_load_lock)); + ASSERT(cmi->cmi_refcnt == 0); + + if (cmi->cmi_prev != NULL) + cmi->cmi_prev = cmi->cmi_next; + + if (cmi->cmi_next != NULL) + cmi->cmi_next->cmi_prev = cmi->cmi_prev; + + if (cmi_list == cmi) + cmi_list = cmi->cmi_next; +} + +/* + * 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. + */ +static void +cmi_hold(cmi_t *cmi) +{ + ASSERT(MUTEX_HELD(&cmi_load_lock)); + + mutex_enter(&mod_lock); + cmi->cmi_modp->mod_ref++; + mutex_exit(&mod_lock); + cmi->cmi_refcnt++; +} + +static void +cmi_rele(cmi_t *cmi) +{ + ASSERT(MUTEX_HELD(&cmi_load_lock)); + + mutex_enter(&mod_lock); + cmi->cmi_modp->mod_ref--; + mutex_exit(&mod_lock); + + if (--cmi->cmi_refcnt == 0) { + cmi_unlink(cmi); + kmem_free(cmi, sizeof (cmi_t)); + } +} + +static cmi_ops_t * +cmi_getops(modctl_t *modp) +{ + cmi_ops_t *ops; + + if ((ops = (cmi_ops_t *)modlookup_by_modctl(modp, "_cmi_ops")) == + NULL) { + cmn_err(CE_WARN, "cpu module '%s' is invalid: no _cmi_ops " + "found", modp->mod_modname); + return (NULL); + } + + if (ops->cmi_init == NULL) { + cmn_err(CE_WARN, "cpu module '%s' is invalid: no cmi_init " + "entry point", modp->mod_modname); + return (NULL); + } + + return (ops); } static cmi_t * cmi_load_modctl(modctl_t *modp) { - uintptr_t ops; + cmi_ops_t *ops; + uintptr_t ver; cmi_t *cmi; + cmi_api_ver_t apiver; ASSERT(MUTEX_HELD(&cmi_load_lock)); @@ -86,160 +210,306 @@ cmi_load_modctl(modctl_t *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", modp->mod_modname); + if ((ver = modlookup_by_modctl(modp, "_cmi_api_version")) == NULL) { + /* + * Apparently a cpu module before versioning was introduced - + * we call this version 0. + */ + apiver = CMI_API_VERSION_0; + } else { + apiver = *((cmi_api_ver_t *)ver); + if (!CMI_API_VERSION_CHKMAGIC(apiver)) { + cmn_err(CE_WARN, "cpu module '%s' is invalid: " + "_cmi_api_version 0x%x has bad magic", + modp->mod_modname, apiver); + return (NULL); + } + } + + if (apiver != CMI_API_VERSION) { + cmn_err(CE_WARN, "cpu module '%s' has API version %d, " + "kernel requires API version %d", modp->mod_modname, + CMI_API_VERSION_TOPRINT(apiver), + CMI_API_VERSION_TOPRINT(CMI_API_VERSION)); 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); + if ((ops = cmi_getops(modp)) == NULL) + return (NULL); - cmi = kmem_zalloc(sizeof (cmi_t), KM_SLEEP); - cmi->cmi_ops = (const cmi_ops_t *)ops; + cmi = kmem_zalloc(sizeof (*cmi), KM_SLEEP); + cmi->cmi_ops = ops; cmi->cmi_modp = modp; - cmi->cmi_next = cmi_list; - cmi_list = cmi; + cmi_link(cmi); return (cmi); } +static int +cmi_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match) +{ + if (match >= CMI_MATCH_VENDOR && + cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2)) + return (0); + + if (match >= CMI_MATCH_FAMILY && + cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2)) + return (0); + + if (match >= CMI_MATCH_MODEL && + cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2)) + return (0); + + if (match >= CMI_MATCH_STEPPING && + cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2)) + return (0); + + return (1); +} + +static int +cmi_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) +{ + cmi_hdl_t thdl = (cmi_hdl_t)arg1; + int match = *((int *)arg2); + cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3; + + if (cmi_cpu_match(thdl, whdl, match)) { + cmi_hdl_hold(whdl); /* short-term hold */ + *rsltp = whdl; + return (CMI_HDL_WALK_DONE); + } else { + return (CMI_HDL_WALK_NEXT); + } +} + static cmi_t * -cmi_load_module(cpu_t *cp) +cmi_search_list(cmi_hdl_t hdl, int match) { - modctl_t *modp; - cmi_t *cmi; - int i, modid; - uint_t s[3]; + cmi_hdl_t dhdl = NULL; + cmi_t *cmi = NULL; - /* - * 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); - } + cmi_hdl_walk(cmi_search_list_cb, (void *)hdl, (void *)&match, &dhdl); + if (dhdl) { + cmi = HDL2CMI(dhdl); + cmi_hdl_rele(dhdl); /* held in cmi_search_list_cb */ } - mutex_exit(&cpu_lock); + return (cmi); +} + +static cmi_t * +cmi_load_module(cmi_hdl_t hdl, int match, int *chosenp) +{ + modctl_t *modp; + cmi_t *cmi; + int modid; + uint_t s[3]; + + ASSERT(MUTEX_HELD(&cmi_load_lock)); + ASSERT(match == CMI_MATCH_STEPPING || match == CMI_MATCH_MODEL || + match == CMI_MATCH_FAMILY || match == CMI_MATCH_VENDOR); /* - * If we can't find a match, attempt to load the appropriate module. - * If that also fails, try to load the generic CPU module. + * Have we already loaded a module for a cpu with the same + * vendor/family/model/stepping? */ - s[0] = cpuid_getfamily(cp); - s[1] = cpuid_getmodel(cp); - s[2] = cpuid_getstep(cp); + if ((cmi = cmi_search_list(hdl, match)) != NULL) { + cmi_hold(cmi); + return (cmi); + } + s[0] = cmi_hdl_family(hdl); + s[1] = cmi_hdl_model(hdl); + s[2] = cmi_hdl_stepping(hdl); 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"); + cmi_hdl_vendorstr(hdl), ".", s, match, chosenp); if (modid == -1) return (NULL); modp = mod_hold_by_id(modid); cmi = cmi_load_modctl(modp); + if (cmi) + cmi_hold(cmi); mod_release_mod(modp); return (cmi); } +/* + * Try to load a cpu module with specific support for this chip type. + */ static cmi_t * -cmi_load_generic(void) +cmi_load_specific(cmi_hdl_t hdl, void **datap) +{ + cmi_t *cmi; + int err; + int i; + + ASSERT(MUTEX_HELD(&cmi_load_lock)); + + for (i = CMI_MATCH_STEPPING; i >= CMI_MATCH_VENDOR; i--) { + int suffixlevel; + + if ((cmi = cmi_load_module(hdl, i, &suffixlevel)) == NULL) + return (NULL); + + /* + * A module has loaded and has a _cmi_ops structure, and the + * module has been held for this instance. Call its cmi_init + * entry point - we expect success (0) or ENOTSUP. + */ + if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) == 0) { + if (boothowto & RB_VERBOSE) { + printf("initialized cpu module '%s' on " + "chip %d core %d strand %d\n", + cmi->cmi_modp->mod_modname, + cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), + cmi_hdl_strandid(hdl)); + } + return (cmi); + } else if (err != ENOTSUP) { + cmn_err(CE_WARN, "failed to init cpu module '%s' on " + "chip %d core %d strand %d: err=%d\n", + cmi->cmi_modp->mod_modname, + cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), + cmi_hdl_strandid(hdl), err); + } + + /* + * The module failed or declined to init, so release + * it and update i to be equal to the number + * of suffices actually used in the last module path. + */ + cmi_rele(cmi); + i = suffixlevel; + } + + return (NULL); +} + +/* + * Load the generic IA32 MCA cpu module, which may still supplement + * itself with model-specific support through cpu model-specific modules. + */ +static cmi_t * +cmi_load_generic(cmi_hdl_t hdl, void **datap) { modctl_t *modp; cmi_t *cmi; int modid; + int err; + + ASSERT(MUTEX_HELD(&cmi_load_lock)); if ((modid = modload(CPUMOD_SUBDIR, CPUMOD_PREFIX ".generic")) == -1) return (NULL); modp = mod_hold_by_id(modid); cmi = cmi_load_modctl(modp); + if (cmi) + cmi_hold(cmi); mod_release_mod(modp); + if (cmi == NULL) + return (NULL); + + if ((err = cmi->cmi_ops->cmi_init(hdl, datap)) != 0) { + if (err != ENOTSUP) + cmn_err(CE_WARN, CPUMOD_PREFIX ".generic failed to " + "init: err=%d", err); + cmi_rele(cmi); + return (NULL); + } + 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) +cmi_hdl_t +cmi_init(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, + uint_t strandid) { - int err = ENOENT; - cmi_t *cmi; + cmi_t *cmi = NULL; + cmi_hdl_t hdl; void *data; + if (cmi_no_init) { + cmi_no_mca_init = 1; + return (NULL); + } + mutex_enter(&cmi_load_lock); - if (!cmi_force_generic && ( - ((cmi = cmi_load_module(cp)) == NULL) || - ((err = cmi->cmi_ops->cmi_init(cp, &data)) != 0 && - err != ENOTSUP))) { - cmn_err(CE_WARN, - "cpu%d: failed to init cpu module '%s': err=%d", - cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err); + if ((hdl = cmi_hdl_create(class, chipid, coreid, strandid)) == NULL) { mutex_exit(&cmi_load_lock); - return (-1); + cmn_err(CE_WARN, "There will be no MCA support on chip %d " + "core %d strand %d (cmi_hdl_create returned NULL)\n", + chipid, coreid, strandid); + return (NULL); } - if ((cmi_force_generic || err != 0) && - ((cmi = cmi_load_generic()) == NULL || - (err = cmi->cmi_ops->cmi_init(cp, &data)) != 0)) { - cmn_err(CE_WARN, - "cpu%d: failed to init cpu module '%s': err=%d", - cp->cpu_id, cmi ? cmi->cmi_modp->mod_modname : "<>", err); + if (!cmi_force_generic) + cmi = cmi_load_specific(hdl, &data); + + if (cmi == NULL && (cmi = cmi_load_generic(hdl, &data)) == NULL) { + cmn_err(CE_WARN, "There will be no MCA support on chip %d " + "core %d strand %d\n", chipid, coreid, strandid); + cmi_hdl_rele(hdl); mutex_exit(&cmi_load_lock); - return (-1); + return (NULL); } - ASSERT(cp->cpu_m.mcpu_cmi == NULL); - cp->cpu_m.mcpu_cmi = cmi; - cp->cpu_m.mcpu_cmidata = data; + cmi_hdl_setcmi(hdl, cmi, data); - cmi->cmi_refcnt++; - mutex_exit(&cmi_load_lock); + cms_init(hdl); - if (boothowto & RB_VERBOSE) { - printf("cpu%d: initialized cpu module '%s'\n", - cp->cpu_id, cmi->cmi_modp->mod_modname); - } + mutex_exit(&cmi_load_lock); - return (0); + return (hdl); } +/* + * cmi_fini is not called at the moment. It is intended to be called + * on DR deconfigure of a cpu resource. It should not be called at + * simple offline of a cpu. + */ void -cmi_init(void) +cmi_fini(cmi_hdl_t hdl) { - if (cmi_load(CPU) < 0) - panic("failed to load module for cpu%d", CPU->cpu_id); + cmi_t *cmi = HDL2CMI(hdl); + + if (cms_present(hdl)) + cms_fini(hdl); + + if (CMI_OP_PRESENT(cmi, cmi_fini)) + CMI_OPS(cmi)->cmi_fini(hdl); + + cmi_hdl_rele(hdl); /* release hold obtained in cmi_hdl_create */ } +/* + * cmi_post_startup is called from post_startup for the boot cpu only. + */ void -cmi_post_init(void) +cmi_post_startup(void) { - CMI_OPS(CPU)->cmi_post_init(CMI_DATA(CPU)); + cmi_hdl_t hdl; + cmi_t *cmi; + + if (cmi_no_mca_init != 0 || + (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ + return; + + cmi = HDL2CMI(hdl); + + if (CMI_OP_PRESENT(cmi, cmi_post_startup)) + CMI_OPS(cmi)->cmi_post_startup(hdl); + + cmi_hdl_rele(hdl); } /* @@ -250,96 +520,333 @@ cmi_post_init(void) void cmi_post_mpstartup(void) { - CMI_OPS(CPU)->cmi_post_mpstartup(CMI_DATA(CPU)); + cmi_hdl_t hdl; + cmi_t *cmi; + + if (cmi_no_mca_init != 0 || + (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ + return; + + cmi = HDL2CMI(hdl); + + if (CMI_OP_PRESENT(cmi, cmi_post_mpstartup)) + CMI_OPS(cmi)->cmi_post_mpstartup(hdl); + + cmi_hdl_rele(hdl); +} + +void +cmi_faulted_enter(cmi_hdl_t hdl) +{ + cmi_t *cmi = HDL2CMI(hdl); + + if (cmi_no_mca_init != 0) + return; + + if (CMI_OP_PRESENT(cmi, cmi_faulted_enter)) + CMI_OPS(cmi)->cmi_faulted_enter(hdl); } void -cmi_faulted_enter(cpu_t *cp) +cmi_faulted_exit(cmi_hdl_t hdl) { - CMI_OPS(cp)->cmi_faulted_enter(CMI_DATA(cp)); + cmi_t *cmi = HDL2CMI(hdl); + + if (cmi_no_mca_init != 0) + return; + + if (CMI_OP_PRESENT(cmi, cmi_faulted_exit)) + CMI_OPS(cmi)->cmi_faulted_exit(hdl); } void -cmi_faulted_exit(cpu_t *cp) +cmi_mca_init(cmi_hdl_t hdl) { - CMI_OPS(cp)->cmi_faulted_exit(CMI_DATA(cp)); + cmi_t *cmi; + + if (cmi_no_mca_init != 0) + return; + + cmi = HDL2CMI(hdl); + + if (CMI_OP_PRESENT(cmi, cmi_mca_init)) + CMI_OPS(cmi)->cmi_mca_init(hdl); } +#define CMI_RESPONSE_PANIC 0x0 /* panic must have value 0 */ +#define CMI_RESPONSE_NONE 0x1 +#define CMI_RESPONSE_CKILL 0x2 +#define CMI_RESPONSE_REBOOT 0x3 /* not implemented */ +#define CMI_RESPONSE_ONTRAP_PROT 0x4 +#define CMI_RESPONSE_LOFAULT_PROT 0x5 + +/* + * Return 0 if we will panic in response to this machine check, otherwise + * non-zero. If the caller is cmi_mca_trap in this file then the nonzero + * return values are to be interpreted from CMI_RESPONSE_* above. + * + * This function must just return what will be done without actually + * doing anything; this includes not changing the regs. + */ int -cmi_scrubber_enable(cpu_t *cp, uint64_t base, uint64_t ilen, int cscontig) +cmi_mce_response(struct regs *rp, uint64_t disp) { - return (CMI_OPS(cp)->cmi_scrubber_enable(CMI_DATA(cp), base, ilen, - cscontig)); + int panicrsp = cmi_panic_on_uncorrectable_error ? CMI_RESPONSE_PANIC : + CMI_RESPONSE_NONE; + on_trap_data_t *otp; + + ASSERT(rp != NULL); /* don't call for polling, only on #MC */ + + /* + * If no bits are set in the disposition then there is nothing to + * worry about and we do not need to trampoline to ontrap or + * lofault handlers. + */ + if (disp == 0) + return (CMI_RESPONSE_NONE); + + /* + * Unconstrained errors cannot be forgiven, even by ontrap or + * lofault protection. The data is not poisoned and may not + * even belong to the trapped context - eg a writeback of + * data that is found to be bad. + */ + if (disp & CMI_ERRDISP_UC_UNCONSTRAINED) + return (panicrsp); + + /* + * ontrap OT_DATA_EC and lofault protection forgive any disposition + * other than unconstrained, even those normally forced fatal. + */ + if ((otp = curthread->t_ontrap) != NULL && otp->ot_prot & OT_DATA_EC) + return (CMI_RESPONSE_ONTRAP_PROT); + else if (curthread->t_lofault) + return (CMI_RESPONSE_LOFAULT_PROT); + + /* + * Forced-fatal errors are terminal even in user mode. + */ + if (disp & CMI_ERRDISP_FORCEFATAL) + return (panicrsp); + + /* + * If the trapped context is corrupt or we have no instruction pointer + * to resume at (and aren't trampolining to a fault handler) + * then in the kernel case we must panic and in usermode we + * kill the affected contract. + */ + if (disp & (CMI_ERRDISP_CURCTXBAD | CMI_ERRDISP_RIPV_INVALID)) + return (USERMODE(rp->r_cs) ? CMI_RESPONSE_CKILL : panicrsp); + + /* + * Anything else is harmless + */ + return (CMI_RESPONSE_NONE); } -void -cmi_mca_init(void) +int cma_mca_trap_panic_suppressed = 0; + +static void +cmi_mca_panic(void) { - CMI_OPS(CPU)->cmi_mca_init(CMI_DATA(CPU)); + if (cmi_panic_on_uncorrectable_error) { + fm_panic("Unrecoverable Machine-Check Exception"); + } else { + cmn_err(CE_WARN, "suppressing panic from fatal #mc"); + cma_mca_trap_panic_suppressed++; + } } + +int cma_mca_trap_contract_kills = 0; +int cma_mca_trap_ontrap_forgiven = 0; +int cma_mca_trap_lofault_forgiven = 0; + +/* + * Native #MC handler - we branch to here from mcetrap + */ +/*ARGSUSED*/ 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"); +#ifndef __xpv + cmi_hdl_t hdl = NULL; + uint64_t disp; + cmi_t *cmi; + int s; + + if (cmi_no_mca_init != 0) + return; + + /* + * This function can call cmn_err, and the cpu module cmi_mca_trap + * entry point may also elect to call cmn_err (e.g., if it can't + * log the error onto an errorq, say very early in boot). + * We need to let cprintf know that we must not block. + */ + s = spl8(); + + if ((hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), + cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) == NULL || + (cmi = HDL2CMI(hdl)) == NULL || + !CMI_OP_PRESENT(cmi, cmi_mca_trap)) { + + cmn_err(CE_WARN, "#MC exception on cpuid %d: %s", + CPU->cpu_id, + hdl ? "handle lookup ok but no #MC handler found" : + "handle lookup failed"); + + if (hdl != NULL) + cmi_hdl_rele(hdl); + + splx(s); + return; } -} -int -cmi_mca_inject(cmi_mca_regs_t *regs, uint_t nregs) -{ - int err; + disp = CMI_OPS(cmi)->cmi_mca_trap(hdl, rp); + + switch (cmi_mce_response(rp, disp)) { + default: + cmn_err(CE_WARN, "Invalid response from cmi_mce_response"); + /*FALLTHRU*/ - kpreempt_disable(); - err = CMI_OPS(CPU)->cmi_mca_inject(CMI_DATA(CPU), regs, nregs); - kpreempt_enable(); + case CMI_RESPONSE_PANIC: + cmi_mca_panic(); + break; - return (err); + case CMI_RESPONSE_NONE: + break; + + case CMI_RESPONSE_CKILL: + ttolwp(curthread)->lwp_pcb.pcb_flags |= ASYNC_HWERR; + aston(curthread); + cma_mca_trap_contract_kills++; + break; + + case CMI_RESPONSE_ONTRAP_PROT: { + on_trap_data_t *otp = curthread->t_ontrap; + otp->ot_trap = OT_DATA_EC; + rp->r_pc = otp->ot_trampoline; + cma_mca_trap_ontrap_forgiven++; + break; + } + + case CMI_RESPONSE_LOFAULT_PROT: + rp->r_r0 = EFAULT; + rp->r_pc = curthread->t_lofault; + cma_mca_trap_lofault_forgiven++; + break; + } + + cmi_hdl_rele(hdl); + splx(s); +#endif /* __xpv */ } void -cmi_mca_poke(void) +cmi_hdl_poke(cmi_hdl_t hdl) { - CMI_OPS(CPU)->cmi_mca_poke(CMI_DATA(CPU)); + cmi_t *cmi = HDL2CMI(hdl); + + if (!CMI_OP_PRESENT(cmi, cmi_hdl_poke)) + return; + + CMI_OPS(cmi)->cmi_hdl_poke(hdl); } void -cmi_mc_register(cpu_t *cp, const cmi_mc_ops_t *mcops, void *mcdata) +cmi_mc_register(cmi_hdl_t hdl, const cmi_mc_ops_t *mcops, void *mcdata) { - CMI_OPS(cp)->cmi_mc_register(CMI_DATA(cp), mcops, mcdata); + if (!cmi_no_mca_init) + cmi_hdl_setmc(hdl, mcops, mcdata); } -int +cmi_errno_t cmi_mc_patounum(uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, uint32_t synd, int syndtype, mc_unum_t *up) { const struct cmi_mc_ops *mcops; - cpu_t *cp = CPU; + cmi_hdl_t hdl; + cmi_errno_t rv; + + if (cmi_no_mca_init || + (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ + return (CMIERR_MC_ABSENT); + + if ((mcops = cmi_hdl_getmcops(hdl)) == NULL || + mcops->cmi_mc_patounum == NULL) { + cmi_hdl_rele(hdl); + return (CMIERR_MC_NOTSUP); + } - if (CMI_OPS(cp) == NULL || - (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) - return (-1); /* not registered yet */ + rv = mcops->cmi_mc_patounum(cmi_hdl_getmcdata(hdl), pa, valid_hi, + valid_lo, synd, syndtype, up); - return (mcops->cmi_mc_patounum(CMI_DATA(cp), pa, valid_hi, valid_lo, - synd, syndtype, up)); + cmi_hdl_rele(hdl); + + return (rv); } -int +cmi_errno_t cmi_mc_unumtopa(mc_unum_t *up, nvlist_t *nvl, uint64_t *pap) { const struct cmi_mc_ops *mcops; - cpu_t *cp = CPU; + cmi_hdl_t hdl; + cmi_errno_t rv; if (up != NULL && nvl != NULL) - return (-1); /* only convert from one or the other form */ + return (CMIERR_API); /* convert from just one form */ + + if (cmi_no_mca_init || + (hdl = cmi_hdl_any()) == NULL) /* short-term hold */ + return (CMIERR_MC_ABSENT); + + if ((mcops = cmi_hdl_getmcops(hdl)) == NULL || + mcops->cmi_mc_unumtopa == NULL) { + cmi_hdl_rele(hdl); + + if (nvl != NULL && nvlist_lookup_uint64(nvl, + FM_FMRI_MEM_PHYSADDR, pap) == 0) { + return (CMIERR_MC_PARTIALUNUMTOPA); + } else { + return (mcops && mcops->cmi_mc_unumtopa ? + CMIERR_MC_NOTSUP : CMIERR_MC_ABSENT); + } + } - if (CMI_OPS(cp) == NULL || - (mcops = CMI_OPS(cp)->cmi_mc_getops(CMI_DATA(cp))) == NULL) - return (-1); /* not registered yet */ + rv = mcops->cmi_mc_unumtopa(cmi_hdl_getmcdata(hdl), up, nvl, pap); - return (mcops->cmi_mc_unumtopa(CMI_DATA(cp), up, nvl, pap)); + cmi_hdl_rele(hdl); + + return (rv); +} + +void +cmi_mc_logout(cmi_hdl_t hdl, boolean_t ismc, boolean_t sync) +{ + const struct cmi_mc_ops *mcops; + + if (cmi_no_mca_init || (mcops = cmi_hdl_getmcops(hdl)) == NULL) + return; + + if (mcops->cmi_mc_logout != NULL) + mcops->cmi_mc_logout(hdl, ismc, sync); +} + +cmi_errno_t +cmi_hdl_msrinject(cmi_hdl_t hdl, cmi_mca_regs_t *regs, uint_t nregs, + int force) +{ + cmi_t *cmi = cmi_hdl_getcmi(hdl); + + if (!CMI_OP_PRESENT(cmi, cmi_msrinject)) + return (CMIERR_NOTSUP); + + return (CMI_OPS(cmi)->cmi_msrinject(hdl, regs, nregs, force)); +} + +boolean_t +cmi_panic_on_ue(void) +{ + return (cmi_panic_on_uncorrectable_error ? B_TRUE : B_FALSE); } diff --git a/usr/src/uts/i86pc/os/cmi_hw.c b/usr/src/uts/i86pc/os/cmi_hw.c new file mode 100644 index 0000000000..fe0eb58620 --- /dev/null +++ b/usr/src/uts/i86pc/os/cmi_hw.c @@ -0,0 +1,1240 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * CPU Module Interface - hardware abstraction. + */ + +#include <sys/types.h> +#include <sys/cpu_module.h> +#include <sys/kmem.h> +#include <sys/x86_archext.h> +#include <sys/cpuvar.h> +#include <sys/ksynch.h> +#include <sys/x_call.h> +#include <sys/pghw.h> +#include <sys/pci_cfgspace.h> +#include <sys/archsystm.h> +#include <sys/ontrap.h> +#include <sys/controlregs.h> +#include <sys/sunddi.h> + +/* + * Outside of this file consumers use the opaque cmi_hdl_t. This + * definition is duplicated in the generic_cpu mdb module, so keep + * them in-sync when making changes. + */ +typedef struct cmi_hdl_impl { + enum cmi_hdl_class cmih_class; /* Handle nature */ + struct cmi_hdl_ops *cmih_ops; /* Operations vector */ + uint_t cmih_chipid; /* Chipid of cpu resource */ + uint_t cmih_coreid; /* Core within die */ + uint_t cmih_strandid; /* Thread within core */ + volatile uint32_t *cmih_refcntp; /* Reference count pointer */ + uint64_t cmih_msrsrc; /* MSR data source flags */ + void *cmih_hdlpriv; /* cmi_hw.c private data */ + void *cmih_spec; /* cmi_hdl_{set,get}_specific */ + void *cmih_cmi; /* cpu mod control structure */ + void *cmih_cmidata; /* cpu mod private data */ + const struct cmi_mc_ops *cmih_mcops; /* Memory-controller ops */ + void *cmih_mcdata; /* Memory-controller data */ +} cmi_hdl_impl_t; + +#define IMPLHDL(ophdl) ((cmi_hdl_impl_t *)ophdl) + +/* + * Handles are looked up from contexts such as polling, injection etc + * where the context is reasonably well defined (although a poller could + * interrupt any old thread holding any old lock). They are also looked + * up by machine check handlers, which may strike at inconvenient times + * such as during handle initialization or destruction or during handle + * lookup (which the #MC handler itself will also have to perform). + * + * So keeping handles in a linked list makes locking difficult when we + * consider #MC handlers. Our solution is to have an array indexed + * by that which uniquely identifies a handle - chip/core/strand id - + * with each array member a structure including a pointer to a handle + * structure for the resource, and a reference count for the handle. + * Reference counts are modified atomically. The public cmi_hdl_hold + * always succeeds because this can only be used after handle creation + * and before the call to destruct, so the hold count it already at least one. + * In other functions that lookup a handle (cmi_hdl_lookup, cmi_hdl_any) + * we must be certain that the count has not already decrmented to zero + * before applying our hold. + * + * This array is allocated when first we want to populate an entry. + * When allocated it is maximal - ideally we should scale to the + * actual number of chips, cores per chip and strand per core but + * that info is not readily available if we are virtualized so + * for now we stick with the dumb approach. + */ +#define CMI_MAX_CHIPS 16 +#define CMI_MAX_CORES_PER_CHIP 4 +#define CMI_MAX_STRANDS_PER_CORE 2 +#define CMI_HDL_HASHSZ (CMI_MAX_CHIPS * CMI_MAX_CORES_PER_CHIP * \ + CMI_MAX_STRANDS_PER_CORE) + +struct cmi_hdl_hashent { + volatile uint32_t cmhe_refcnt; + cmi_hdl_impl_t *cmhe_hdlp; +}; + +static struct cmi_hdl_hashent *cmi_hdl_hash; + +#define CMI_HDL_HASHIDX(chipid, coreid, strandid) \ + ((chipid) * CMI_MAX_CHIPS + (coreid) * CMI_MAX_CORES_PER_CHIP + \ + (strandid)) + +/* + * Controls where we will source PCI config space data. + */ +#define CMI_PCICFG_FLAG_RD_HWOK 0x0001 +#define CMI_PCICFG_FLAG_RD_INTERPOSEOK 0X0002 +#define CMI_PCICFG_FLAG_WR_HWOK 0x0004 +#define CMI_PCICFG_FLAG_WR_INTERPOSEOK 0X0008 + +static uint64_t cmi_pcicfg_flags = + CMI_PCICFG_FLAG_RD_HWOK | CMI_PCICFG_FLAG_RD_INTERPOSEOK | + CMI_PCICFG_FLAG_WR_HWOK | CMI_PCICFG_FLAG_WR_INTERPOSEOK; + +/* + * The flags for individual cpus are kept in their per-cpu handle cmih_msrsrc + */ +#define CMI_MSR_FLAG_RD_HWOK 0x0001 +#define CMI_MSR_FLAG_RD_INTERPOSEOK 0x0002 +#define CMI_MSR_FLAG_WR_HWOK 0x0004 +#define CMI_MSR_FLAG_WR_INTERPOSEOK 0x0008 + +int cmi_call_func_ntv_tries = 3; + +static cmi_errno_t +call_func_ntv(int cpuid, xc_func_t func, xc_arg_t arg1, xc_arg_t arg2) +{ + cmi_errno_t rc = -1; + int i; + + kpreempt_disable(); + + if (CPU->cpu_id == cpuid) { + (*func)(arg1, arg2, (xc_arg_t)&rc); + } else { + /* + * This should not happen for a #MC trap or a poll, so + * this is likely an error injection or similar. + * We will try to cross call with xc_trycall - we + * can't guarantee success with xc_call because + * the interrupt code in the case of a #MC may + * already hold the xc mutex. + */ + for (i = 0; i < cmi_call_func_ntv_tries; i++) { + cpuset_t cpus; + + CPUSET_ONLY(cpus, cpuid); + xc_trycall(arg1, arg2, (xc_arg_t)&rc, cpus, func); + if (rc != -1) + break; + + DELAY(1); + } + } + + kpreempt_enable(); + + return (rc != -1 ? rc : CMIERR_DEADLOCK); +} + +/* + * ======================================================= + * | MSR Interposition | + * | ----------------- | + * | | + * ------------------------------------------------------- + */ + +#define CMI_MSRI_HASHSZ 16 +#define CMI_MSRI_HASHIDX(hdl, msr) \ + (((uintptr_t)(hdl) >> 3 + (msr)) % (CMI_MSRI_HASHSZ - 1)) + +struct cmi_msri_bkt { + kmutex_t msrib_lock; + struct cmi_msri_hashent *msrib_head; +}; + +struct cmi_msri_hashent { + struct cmi_msri_hashent *msrie_next; + struct cmi_msri_hashent *msrie_prev; + cmi_hdl_impl_t *msrie_hdl; + uint_t msrie_msrnum; + uint64_t msrie_msrval; +}; + +#define CMI_MSRI_MATCH(ent, hdl, req_msr) \ + ((ent)->msrie_hdl == (hdl) && (ent)->msrie_msrnum == (req_msr)) + +static struct cmi_msri_bkt msrihash[CMI_MSRI_HASHSZ]; + +static void +msri_addent(cmi_hdl_impl_t *hdl, cmi_mca_regs_t *regp) +{ + int idx = CMI_MSRI_HASHIDX(hdl, regp->cmr_msrnum); + struct cmi_msri_bkt *hbp = &msrihash[idx]; + struct cmi_msri_hashent *hep; + + mutex_enter(&hbp->msrib_lock); + + for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { + if (CMI_MSRI_MATCH(hep, hdl, regp->cmr_msrnum)) + break; + } + + if (hep != NULL) { + hep->msrie_msrval = regp->cmr_msrval; + } else { + hep = kmem_alloc(sizeof (*hep), KM_SLEEP); + hep->msrie_hdl = hdl; + hep->msrie_msrnum = regp->cmr_msrnum; + hep->msrie_msrval = regp->cmr_msrval; + + if (hbp->msrib_head != NULL) + hbp->msrib_head->msrie_prev = hep; + hep->msrie_next = hbp->msrib_head; + hep->msrie_prev = NULL; + hbp->msrib_head = hep; + } + + mutex_exit(&hbp->msrib_lock); +} + +/* + * Look for a match for the given hanlde and msr. Return 1 with valp + * filled if a match is found, otherwise return 0 with valp untouched. + */ +static int +msri_lookup(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t *valp) +{ + int idx = CMI_MSRI_HASHIDX(hdl, msr); + struct cmi_msri_bkt *hbp = &msrihash[idx]; + struct cmi_msri_hashent *hep; + + /* + * This function is called during #MC trap handling, so we should + * consider the possibility that the hash mutex is held by the + * interrupted thread. This should not happen because interposition + * is an artificial injection mechanism and the #MC is requested + * after adding entries, but just in case of a real #MC at an + * unlucky moment we'll use mutex_tryenter here. + */ + if (!mutex_tryenter(&hbp->msrib_lock)) + return (0); + + for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { + if (CMI_MSRI_MATCH(hep, hdl, msr)) { + *valp = hep->msrie_msrval; + break; + } + } + + mutex_exit(&hbp->msrib_lock); + + return (hep != NULL); +} + +/* + * Remove any interposed value that matches. + */ +static void +msri_rment(cmi_hdl_impl_t *hdl, uint_t msr) +{ + + int idx = CMI_MSRI_HASHIDX(hdl, msr); + struct cmi_msri_bkt *hbp = &msrihash[idx]; + struct cmi_msri_hashent *hep; + + if (!mutex_tryenter(&hbp->msrib_lock)) + return; + + for (hep = hbp->msrib_head; hep != NULL; hep = hep->msrie_next) { + if (CMI_MSRI_MATCH(hep, hdl, msr)) { + if (hep->msrie_prev != NULL) + hep->msrie_prev->msrie_next = hep->msrie_next; + + if (hep->msrie_next != NULL) + hep->msrie_next->msrie_prev = hep->msrie_prev; + + if (hbp->msrib_head == hep) + hbp->msrib_head = hep->msrie_next; + + kmem_free(hep, sizeof (*hep)); + break; + } + } + + mutex_exit(&hbp->msrib_lock); +} + +/* + * ======================================================= + * | PCI Config Space Interposition | + * | ------------------------------ | + * | | + * ------------------------------------------------------- + */ + +/* + * Hash for interposed PCI config space values. We lookup on bus/dev/fun/offset + * and then record whether the value stashed was made with a byte, word or + * doubleword access; we will only return a hit for an access of the + * same size. If you access say a 32-bit register using byte accesses + * and then attempt to read the full 32-bit value back you will not obtain + * any sort of merged result - you get a lookup miss. + */ + +#define CMI_PCII_HASHSZ 16 +#define CMI_PCII_HASHIDX(b, d, f, o) \ + (((b) + (d) + (f) + (o)) % (CMI_PCII_HASHSZ - 1)) + +struct cmi_pcii_bkt { + kmutex_t pciib_lock; + struct cmi_pcii_hashent *pciib_head; +}; + +struct cmi_pcii_hashent { + struct cmi_pcii_hashent *pcii_next; + struct cmi_pcii_hashent *pcii_prev; + int pcii_bus; + int pcii_dev; + int pcii_func; + int pcii_reg; + int pcii_asize; + uint32_t pcii_val; +}; + +#define CMI_PCII_MATCH(ent, b, d, f, r, asz) \ + ((ent)->pcii_bus == (b) && (ent)->pcii_dev == (d) && \ + (ent)->pcii_func == (f) && (ent)->pcii_reg == (r) && \ + (ent)->pcii_asize == (asz)) + +static struct cmi_pcii_bkt pciihash[CMI_PCII_HASHSZ]; + + +/* + * Add a new entry to the PCI interpose hash, overwriting any existing + * entry that is found. + */ +static void +pcii_addent(int bus, int dev, int func, int reg, uint32_t val, int asz) +{ + int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); + struct cmi_pcii_bkt *hbp = &pciihash[idx]; + struct cmi_pcii_hashent *hep; + + mutex_enter(&hbp->pciib_lock); + + for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { + if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) + break; + } + + if (hep != NULL) { + hep->pcii_val = val; + } else { + hep = kmem_alloc(sizeof (*hep), KM_SLEEP); + hep->pcii_bus = bus; + hep->pcii_dev = dev; + hep->pcii_func = func; + hep->pcii_reg = reg; + hep->pcii_asize = asz; + hep->pcii_val = val; + + if (hbp->pciib_head != NULL) + hbp->pciib_head->pcii_prev = hep; + hep->pcii_next = hbp->pciib_head; + hep->pcii_prev = NULL; + hbp->pciib_head = hep; + } + + mutex_exit(&hbp->pciib_lock); +} + +/* + * Look for a match for the given bus/dev/func/reg; return 1 with valp + * filled if a match is found, otherwise return 0 with valp untouched. + */ +static int +pcii_lookup(int bus, int dev, int func, int reg, int asz, uint32_t *valp) +{ + int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); + struct cmi_pcii_bkt *hbp = &pciihash[idx]; + struct cmi_pcii_hashent *hep; + + if (!mutex_tryenter(&hbp->pciib_lock)) + return (0); + + for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { + if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) { + *valp = hep->pcii_val; + break; + } + } + + mutex_exit(&hbp->pciib_lock); + + return (hep != NULL); +} + +static void +pcii_rment(int bus, int dev, int func, int reg, int asz) +{ + int idx = CMI_PCII_HASHIDX(bus, dev, func, reg); + struct cmi_pcii_bkt *hbp = &pciihash[idx]; + struct cmi_pcii_hashent *hep; + + mutex_enter(&hbp->pciib_lock); + + for (hep = hbp->pciib_head; hep != NULL; hep = hep->pcii_next) { + if (CMI_PCII_MATCH(hep, bus, dev, func, reg, asz)) { + if (hep->pcii_prev != NULL) + hep->pcii_prev->pcii_next = hep->pcii_next; + + if (hep->pcii_next != NULL) + hep->pcii_next->pcii_prev = hep->pcii_prev; + + if (hbp->pciib_head == hep) + hbp->pciib_head = hep->pcii_next; + + kmem_free(hep, sizeof (*hep)); + break; + } + } + + mutex_exit(&hbp->pciib_lock); +} + +/* + * ======================================================= + * | Native methods | + * | -------------- | + * | | + * | These are used when we are running native on bare- | + * | metal, or simply don't know any better. | + * --------------------------------------------------------- + */ + +static uint_t +ntv_vendor(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getvendor((cpu_t *)hdl->cmih_hdlpriv)); +} + +static const char * +ntv_vendorstr(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getvendorstr((cpu_t *)hdl->cmih_hdlpriv)); +} + +static uint_t +ntv_family(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getfamily((cpu_t *)hdl->cmih_hdlpriv)); +} + +static uint_t +ntv_model(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getmodel((cpu_t *)hdl->cmih_hdlpriv)); +} + +static uint_t +ntv_stepping(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getstep((cpu_t *)hdl->cmih_hdlpriv)); +} + +static uint_t +ntv_chipid(cmi_hdl_impl_t *hdl) +{ + return (hdl->cmih_chipid); + +} + +static uint_t +ntv_coreid(cmi_hdl_impl_t *hdl) +{ + return (hdl->cmih_coreid); +} + +static uint_t +ntv_strandid(cmi_hdl_impl_t *hdl) +{ + return (hdl->cmih_strandid); +} + +static uint32_t +ntv_chiprev(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getchiprev((cpu_t *)hdl->cmih_hdlpriv)); +} + +static const char * +ntv_chiprevstr(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getchiprevstr((cpu_t *)hdl->cmih_hdlpriv)); +} + +static uint32_t +ntv_getsockettype(cmi_hdl_impl_t *hdl) +{ + return (cpuid_getsockettype((cpu_t *)hdl->cmih_hdlpriv)); +} + +/*ARGSUSED*/ +static int +ntv_getcr4_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) +{ + ulong_t *dest = (ulong_t *)arg1; + cmi_errno_t *rcp = (cmi_errno_t *)arg3; + + *dest = getcr4(); + *rcp = CMI_SUCCESS; + + return (0); +} + +static ulong_t +ntv_getcr4(cmi_hdl_impl_t *hdl) +{ + cpu_t *cp = (cpu_t *)hdl->cmih_hdlpriv; + ulong_t val; + + (void) call_func_ntv(cp->cpu_id, ntv_getcr4_xc, (xc_arg_t)&val, NULL); + + return (val); +} + +/*ARGSUSED*/ +static int +ntv_setcr4_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) +{ + ulong_t val = (ulong_t)arg1; + cmi_errno_t *rcp = (cmi_errno_t *)arg3; + + setcr4(val); + *rcp = CMI_SUCCESS; + + return (0); +} + +static void +ntv_setcr4(cmi_hdl_impl_t *hdl, ulong_t val) +{ + cpu_t *cp = (cpu_t *)hdl->cmih_hdlpriv; + + (void) call_func_ntv(cp->cpu_id, ntv_setcr4_xc, (xc_arg_t)val, NULL); +} + +/*ARGSUSED*/ +static int +ntv_rdmsr_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) +{ + uint_t msr = (uint_t)arg1; + uint64_t *valp = (uint64_t *)arg2; + cmi_errno_t *rcp = (cmi_errno_t *)arg3; + + on_trap_data_t otd; + + if (on_trap(&otd, OT_DATA_ACCESS) == 0) { + if (checked_rdmsr(msr, valp) == 0) + *rcp = CMI_SUCCESS; + else + *rcp = CMIERR_NOTSUP; + } else { + *rcp = CMIERR_MSRGPF; + } + no_trap(); + + return (0); +} + +static cmi_errno_t +ntv_rdmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t *valp) +{ + cpu_t *cp = (cpu_t *)hdl->cmih_hdlpriv; + + return (call_func_ntv(cp->cpu_id, ntv_rdmsr_xc, + (xc_arg_t)msr, (xc_arg_t)valp)); +} + +/*ARGSUSED*/ +static int +ntv_wrmsr_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) +{ + uint_t msr = (uint_t)arg1; + uint64_t val = *((uint64_t *)arg2); + cmi_errno_t *rcp = (cmi_errno_t *)arg3; + on_trap_data_t otd; + + if (on_trap(&otd, OT_DATA_ACCESS) == 0) { + if (checked_wrmsr(msr, val) == 0) + *rcp = CMI_SUCCESS; + else + *rcp = CMIERR_NOTSUP; + } else { + *rcp = CMIERR_MSRGPF; + } + no_trap(); + + return (0); + +} + +static cmi_errno_t +ntv_wrmsr(cmi_hdl_impl_t *hdl, uint_t msr, uint64_t val) +{ + cpu_t *cp = (cpu_t *)hdl->cmih_hdlpriv; + + return (call_func_ntv(cp->cpu_id, ntv_wrmsr_xc, + (xc_arg_t)msr, (xc_arg_t)&val)); +} + +/*ARGSUSED*/ +static int +ntv_mcheck_xc(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3) +{ + cmi_errno_t *rcp = (cmi_errno_t *)arg3; + + int18(); + *rcp = CMI_SUCCESS; + + return (0); +} + +static void +ntv_mcheck(cmi_hdl_impl_t *hdl) +{ + cpu_t *cp = (cpu_t *)hdl->cmih_hdlpriv; + + (void) call_func_ntv(cp->cpu_id, ntv_mcheck_xc, NULL, NULL); +} + +/* + * Ops structure for handle operations. + */ +struct cmi_hdl_ops { + uint_t (*cmio_vendor)(cmi_hdl_impl_t *); + const char *(*cmio_vendorstr)(cmi_hdl_impl_t *); + uint_t (*cmio_family)(cmi_hdl_impl_t *); + uint_t (*cmio_model)(cmi_hdl_impl_t *); + uint_t (*cmio_stepping)(cmi_hdl_impl_t *); + uint_t (*cmio_chipid)(cmi_hdl_impl_t *); + uint_t (*cmio_coreid)(cmi_hdl_impl_t *); + uint_t (*cmio_strandid)(cmi_hdl_impl_t *); + uint32_t (*cmio_chiprev)(cmi_hdl_impl_t *); + const char *(*cmio_chiprevstr)(cmi_hdl_impl_t *); + uint32_t (*cmio_getsockettype)(cmi_hdl_impl_t *); + ulong_t (*cmio_getcr4)(cmi_hdl_impl_t *); + void (*cmio_setcr4)(cmi_hdl_impl_t *, ulong_t); + cmi_errno_t (*cmio_rdmsr)(cmi_hdl_impl_t *, uint_t, uint64_t *); + cmi_errno_t (*cmio_wrmsr)(cmi_hdl_impl_t *, uint_t, uint64_t); + void (*cmio_mcheck)(cmi_hdl_impl_t *); +} cmi_hdl_ops[] = { + /* + * CMI_HDL_NATIVE - ops when apparently running on bare-metal + */ + { + ntv_vendor, + ntv_vendorstr, + ntv_family, + ntv_model, + ntv_stepping, + ntv_chipid, + ntv_coreid, + ntv_strandid, + ntv_chiprev, + ntv_chiprevstr, + ntv_getsockettype, + ntv_getcr4, + ntv_setcr4, + ntv_rdmsr, + ntv_wrmsr, + ntv_mcheck + }, +}; + +#ifndef __xpv +static void * +cpu_search(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, + uint_t strandid) +{ + switch (class) { + case CMI_HDL_NATIVE: { + cpu_t *cp, *startcp; + + kpreempt_disable(); + cp = startcp = CPU; + do { + if (cmi_ntv_hwchipid(cp) == chipid && + cmi_ntv_hwcoreid(cp) == coreid && + cmi_ntv_hwstrandid(cp) == strandid) { + kpreempt_enable(); + return ((void *)cp); + } + + cp = cp->cpu_next; + } while (cp != startcp); + kpreempt_enable(); + return (NULL); + } + + default: + return (NULL); + } +} +#endif + +cmi_hdl_t +cmi_hdl_create(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, + uint_t strandid) +{ + cmi_hdl_impl_t *hdl; + void *priv = NULL; + int idx; + + if (chipid > CMI_MAX_CHIPS - 1 || coreid > CMI_MAX_CORES_PER_CHIP - 1 || + strandid > CMI_MAX_STRANDS_PER_CORE - 1) + return (NULL); + +#ifndef __xpv + if ((priv = cpu_search(class, chipid, coreid, strandid)) == NULL) + return (NULL); +#endif + + hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP); + + hdl->cmih_class = class; + hdl->cmih_ops = &cmi_hdl_ops[class]; + hdl->cmih_chipid = chipid; + hdl->cmih_coreid = coreid; + hdl->cmih_strandid = strandid; + hdl->cmih_hdlpriv = priv; + hdl->cmih_msrsrc = CMI_MSR_FLAG_RD_HWOK | CMI_MSR_FLAG_RD_INTERPOSEOK | + CMI_MSR_FLAG_WR_HWOK | CMI_MSR_FLAG_WR_INTERPOSEOK; + + ASSERT(hdl->cmih_cmi == NULL && hdl->cmih_cmidata == NULL); + + if (cmi_hdl_hash == NULL) { + size_t sz = CMI_HDL_HASHSZ * sizeof (struct cmi_hdl_hashent); + void *hash = kmem_zalloc(sz, KM_SLEEP); + + if (atomic_cas_ptr(&cmi_hdl_hash, NULL, hash) != NULL) + kmem_free(hash, sz); /* someone beat us */ + } + + idx = CMI_HDL_HASHIDX(chipid, coreid, strandid); + ASSERT(cmi_hdl_hash[idx].cmhe_refcnt == 0 && + cmi_hdl_hash[idx].cmhe_hdlp == NULL); + + /* + * Once we store a nonzero reference count others can find this + * handle via cmi_hdl_lookup etc. This initial hold on the handle + * is to be dropped only if some other part of cmi initialization + * fails or, if it succeeds, at later cpu deconfigure. Note the + * the module private data we hold in cmih_cmi and cmih_cmidata + * is still NULL at this point (the caller will fill it with + * cmi_hdl_setcmi if it initializes) so consumers of handles + * should always be ready for that possibility. + */ + cmi_hdl_hash[idx].cmhe_hdlp = hdl; + hdl->cmih_refcntp = &cmi_hdl_hash[idx].cmhe_refcnt; + cmi_hdl_hash[idx].cmhe_refcnt = 1; + + return ((cmi_hdl_t)hdl); +} + +void +cmi_hdl_hold(cmi_hdl_t ophdl) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + ASSERT(*hdl->cmih_refcntp != 0); /* must not be the initial hold */ + + atomic_inc_32(hdl->cmih_refcntp); +} + +static int +cmi_hdl_canref(int hashidx) +{ + volatile uint32_t *refcntp; + uint32_t refcnt; + + if (cmi_hdl_hash == NULL) + return (0); + + refcntp = &cmi_hdl_hash[hashidx].cmhe_refcnt; + refcnt = *refcntp; + + if (refcnt == 0) { + /* + * Associated object never existed, is being destroyed, + * or has been destroyed. + */ + return (0); + } + + /* + * We cannot use atomic increment here because once the reference + * count reaches zero it must never be bumped up again. + */ + while (refcnt != 0) { + if (atomic_cas_32(refcntp, refcnt, refcnt + 1) == refcnt) + return (1); + refcnt = *refcntp; + } + + /* + * Somebody dropped the reference count to 0 after our initial + * check. + */ + return (0); +} + + +void +cmi_hdl_rele(cmi_hdl_t ophdl) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + int idx; + + ASSERT(*hdl->cmih_refcntp > 0); + + if (atomic_dec_32_nv(hdl->cmih_refcntp) > 0) + return; + + idx = CMI_HDL_HASHIDX(hdl->cmih_chipid, hdl->cmih_coreid, + hdl->cmih_strandid); + cmi_hdl_hash[idx].cmhe_hdlp = NULL; + + kmem_free(hdl, sizeof (*hdl)); +} + +void +cmi_hdl_setspecific(cmi_hdl_t ophdl, void *arg) +{ + IMPLHDL(ophdl)->cmih_spec = arg; +} + +void * +cmi_hdl_getspecific(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_spec); +} + +void +cmi_hdl_setmc(cmi_hdl_t ophdl, const struct cmi_mc_ops *mcops, void *mcdata) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + ASSERT(hdl->cmih_mcops == NULL && hdl->cmih_mcdata == NULL); + hdl->cmih_mcops = mcops; + hdl->cmih_mcdata = mcdata; +} + +const struct cmi_mc_ops * +cmi_hdl_getmcops(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_mcops); +} + +void * +cmi_hdl_getmcdata(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_mcdata); +} + +cmi_hdl_t +cmi_hdl_lookup(enum cmi_hdl_class class, uint_t chipid, uint_t coreid, + uint_t strandid) +{ + int idx = CMI_HDL_HASHIDX(chipid, coreid, strandid); + + if (!cmi_hdl_canref(idx)) + return (NULL); + + if (cmi_hdl_hash[idx].cmhe_hdlp->cmih_class != class) { + cmi_hdl_rele((cmi_hdl_t)cmi_hdl_hash[idx].cmhe_hdlp); + return (NULL); + } + + return ((cmi_hdl_t)cmi_hdl_hash[idx].cmhe_hdlp); +} + +cmi_hdl_t +cmi_hdl_any(void) +{ + int i; + + for (i = 0; i < CMI_HDL_HASHSZ; i++) { + if (cmi_hdl_canref(i)) + return ((cmi_hdl_t)cmi_hdl_hash[i].cmhe_hdlp); + } + + return (NULL); +} + +void +cmi_hdl_walk(int (*cbfunc)(cmi_hdl_t, void *, void *, void *), + void *arg1, void *arg2, void *arg3) +{ + int i; + + for (i = 0; i < CMI_HDL_HASHSZ; i++) { + if (cmi_hdl_canref(i)) { + cmi_hdl_impl_t *hdl = cmi_hdl_hash[i].cmhe_hdlp; + + if ((*cbfunc)((cmi_hdl_t)hdl, arg1, arg2, arg3) == + CMI_HDL_WALK_DONE) { + cmi_hdl_rele((cmi_hdl_t)hdl); + break; + } + cmi_hdl_rele((cmi_hdl_t)hdl); + } + } +} + +void +cmi_hdl_setcmi(cmi_hdl_t ophdl, void *cmi, void *cmidata) +{ + IMPLHDL(ophdl)->cmih_cmidata = cmidata; + IMPLHDL(ophdl)->cmih_cmi = cmi; +} + +void * +cmi_hdl_getcmi(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_cmi); +} + +void * +cmi_hdl_getcmidata(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_cmidata); +} + +enum cmi_hdl_class +cmi_hdl_class(cmi_hdl_t ophdl) +{ + return (IMPLHDL(ophdl)->cmih_class); +} + +#define CMI_HDL_OPFUNC(what, type) \ + type \ + cmi_hdl_##what(cmi_hdl_t ophdl) \ + { \ + return (IMPLHDL(ophdl)->cmih_ops-> \ + cmio_##what(IMPLHDL(ophdl))); \ + } + +CMI_HDL_OPFUNC(vendor, uint_t) +CMI_HDL_OPFUNC(vendorstr, const char *) +CMI_HDL_OPFUNC(family, uint_t) +CMI_HDL_OPFUNC(model, uint_t) +CMI_HDL_OPFUNC(stepping, uint_t) +CMI_HDL_OPFUNC(chipid, uint_t) +CMI_HDL_OPFUNC(coreid, uint_t) +CMI_HDL_OPFUNC(strandid, uint_t) +CMI_HDL_OPFUNC(chiprev, uint32_t) +CMI_HDL_OPFUNC(chiprevstr, const char *) +CMI_HDL_OPFUNC(getsockettype, uint32_t) + +void +cmi_hdl_mcheck(cmi_hdl_t ophdl) +{ + IMPLHDL(ophdl)->cmih_ops->cmio_mcheck(IMPLHDL(ophdl)); +} + +#ifndef __xpv +/* + * Return hardware chip instance; cpuid_get_chipid provides this directly. + */ +uint_t +cmi_ntv_hwchipid(cpu_t *cp) +{ + return (cpuid_get_chipid(cp)); +} + +/* + * Return core instance within a single chip. cpuid_get_coreid numbers cores + * across all chips with the same number of cores on each chip and counting + * all cores of chip N before moving on to count the cores of chip N + 1. + */ +uint_t +cmi_ntv_hwcoreid(cpu_t *cp) +{ + return (cpuid_get_coreid(cp) % cpuid_get_ncore_per_chip(cp)); +} + +/* + * Return strand number within a single core. cpuid_get_clogid numbers + * all execution units (strands, or cores in unstranded models) sequentially + * within a single chip. + */ +uint_t +cmi_ntv_hwstrandid(cpu_t *cp) +{ + int strands_per_core = cpuid_get_ncpu_per_chip(cp) / + cpuid_get_ncore_per_chip(cp); + + return (cpuid_get_clogid(cp) % strands_per_core); +} +#endif /* __xpv */ + +void +cmi_hdlconf_rdmsr_nohw(cmi_hdl_t ophdl) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + hdl->cmih_msrsrc &= ~CMI_MSR_FLAG_RD_HWOK; +} + +void +cmi_hdlconf_wrmsr_nohw(cmi_hdl_t ophdl) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + hdl->cmih_msrsrc &= ~CMI_MSR_FLAG_WR_HWOK; +} + +cmi_errno_t +cmi_hdl_rdmsr(cmi_hdl_t ophdl, uint_t msr, uint64_t *valp) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + /* + * Regardless of the handle class, we first check for am + * interposed value. In the xVM case you probably want to + * place interposed values within the hypervisor itself, but + * we still allow interposing them in dom0 for test and bringup + * purposes. + */ + if ((hdl->cmih_msrsrc & CMI_MSR_FLAG_RD_INTERPOSEOK) && + msri_lookup(hdl, msr, valp)) + return (CMI_SUCCESS); + + if (!(hdl->cmih_msrsrc & CMI_MSR_FLAG_RD_HWOK)) + return (CMIERR_INTERPOSE); + + return (hdl->cmih_ops->cmio_rdmsr(hdl, msr, valp)); +} + +cmi_errno_t +cmi_hdl_wrmsr(cmi_hdl_t ophdl, uint_t msr, uint64_t val) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + + /* Invalidate any interposed value */ + msri_rment(hdl, msr); + + if (!(hdl->cmih_msrsrc & CMI_MSR_FLAG_WR_HWOK)) + return (CMI_SUCCESS); + + return (hdl->cmih_ops->cmio_wrmsr(hdl, msr, val)); +} + +void +cmi_hdl_enable_mce(cmi_hdl_t ophdl) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + ulong_t cr4 = hdl->cmih_ops->cmio_getcr4(hdl); + + hdl->cmih_ops->cmio_setcr4(hdl, cr4 | CR4_MCE); +} + +void +cmi_hdl_msrinterpose(cmi_hdl_t ophdl, cmi_mca_regs_t *regs, uint_t nregs) +{ + cmi_hdl_impl_t *hdl = IMPLHDL(ophdl); + int i; + + for (i = 0; i < nregs; i++) + msri_addent(hdl, regs++); +} + +void +cmi_pcird_nohw(void) +{ + cmi_pcicfg_flags &= ~CMI_PCICFG_FLAG_RD_HWOK; +} + +void +cmi_pciwr_nohw(void) +{ + cmi_pcicfg_flags &= ~CMI_PCICFG_FLAG_WR_HWOK; +} + +static uint32_t +cmi_pci_get_cmn(int bus, int dev, int func, int reg, int asz, + int *interpose, ddi_acc_handle_t hdl) +{ + uint32_t val; + + if (cmi_pcicfg_flags & CMI_PCICFG_FLAG_RD_INTERPOSEOK && + pcii_lookup(bus, dev, func, reg, asz, &val)) { + if (interpose) + *interpose = 1; + return (val); + } + if (interpose) + *interpose = 0; + + if (!(cmi_pcicfg_flags & CMI_PCICFG_FLAG_RD_HWOK)) + return (0); + + switch (asz) { + case 1: + if (hdl) + val = pci_config_get8(hdl, (off_t)reg); + else + val = (*pci_getb_func)(bus, dev, func, reg); + break; + case 2: + if (hdl) + val = pci_config_get16(hdl, (off_t)reg); + else + val = (*pci_getw_func)(bus, dev, func, reg); + break; + case 4: + if (hdl) + val = pci_config_get32(hdl, (off_t)reg); + else + val = (*pci_getl_func)(bus, dev, func, reg); + break; + default: + val = 0; + } + return (val); +} + +uint8_t +cmi_pci_getb(int bus, int dev, int func, int reg, int *interpose, + ddi_acc_handle_t hdl) +{ + return ((uint8_t)cmi_pci_get_cmn(bus, dev, func, reg, 1, interpose, + hdl)); +} + +uint16_t +cmi_pci_getw(int bus, int dev, int func, int reg, int *interpose, + ddi_acc_handle_t hdl) +{ + return ((uint16_t)cmi_pci_get_cmn(bus, dev, func, reg, 2, interpose, + hdl)); +} + +uint32_t +cmi_pci_getl(int bus, int dev, int func, int reg, int *interpose, + ddi_acc_handle_t hdl) +{ + return (cmi_pci_get_cmn(bus, dev, func, reg, 4, interpose, hdl)); +} + +void +cmi_pci_interposeb(int bus, int dev, int func, int reg, uint8_t val) +{ + pcii_addent(bus, dev, func, reg, val, 1); +} + +void +cmi_pci_interposew(int bus, int dev, int func, int reg, uint16_t val) +{ + pcii_addent(bus, dev, func, reg, val, 2); +} + +void +cmi_pci_interposel(int bus, int dev, int func, int reg, uint32_t val) +{ + pcii_addent(bus, dev, func, reg, val, 4); +} + +static void +cmi_pci_put_cmn(int bus, int dev, int func, int reg, int asz, + ddi_acc_handle_t hdl, uint32_t val) +{ + /* + * If there is an interposed value for this register invalidate it. + */ + pcii_rment(bus, dev, func, reg, asz); + + if (!(cmi_pcicfg_flags & CMI_PCICFG_FLAG_WR_HWOK)) + return; + + switch (asz) { + case 1: + if (hdl) + pci_config_put8(hdl, (off_t)reg, (uint8_t)val); + else + (*pci_putb_func)(bus, dev, func, reg, (uint8_t)val); + break; + + case 2: + if (hdl) + pci_config_put16(hdl, (off_t)reg, (uint16_t)val); + else + (*pci_putw_func)(bus, dev, func, reg, (uint16_t)val); + break; + + case 4: + if (hdl) + pci_config_put32(hdl, (off_t)reg, val); + else + (*pci_putl_func)(bus, dev, func, reg, val); + break; + + default: + break; + } +} + +extern void +cmi_pci_putb(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, + uint8_t val) +{ + cmi_pci_put_cmn(bus, dev, func, reg, 1, hdl, val); +} + +extern void +cmi_pci_putw(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, + uint16_t val) +{ + cmi_pci_put_cmn(bus, dev, func, reg, 2, hdl, val); +} + +extern void +cmi_pci_putl(int bus, int dev, int func, int reg, ddi_acc_handle_t hdl, + uint32_t val) +{ + cmi_pci_put_cmn(bus, dev, func, reg, 4, hdl, val); +} diff --git a/usr/src/uts/i86pc/os/cms.c b/usr/src/uts/i86pc/os/cms.c new file mode 100644 index 0000000000..721b5a4db7 --- /dev/null +++ b/usr/src/uts/i86pc/os/cms.c @@ -0,0 +1,641 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 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/cpu_module_ms_impl.h> +#include <sys/cpuvar.h> +#include <sys/ksynch.h> +#include <sys/modctl.h> +#include <sys/x86_archext.h> +#include <sys/systm.h> +#include <sys/cmn_err.h> +#include <sys/param.h> +#include <sys/reboot.h> + +/* + * Set to prevent model-specific support from initialising. + */ +int cms_no_model_specific = 0; + +/* + * Subdirectory (relative to the module search path) in which we will + * look for model-specific modules. + */ +#define CPUMOD_MS_SUBDIR "cpu" + +/* + * Cpu model-specific modules have filenames beginning with the following. + */ +#define CPUMOD_MS_PREFIX "cpu_ms" + +#define HDL2CMS(hdl) cms_hdl_getcms(hdl) + +#define CMS_OPS(cms) (cms)->cms_ops +#define CMS_OP_PRESENT(cms, op) ((cms) && CMS_OPS(cms)->op != NULL) + +struct cms_cpuid { + const char *vendor; + uint_t family; + uint_t model; + uint_t stepping; +}; + +#define CMS_MATCH_VENDOR 0 /* Just match on vendor */ +#define CMS_MATCH_FAMILY 1 /* Match down to family */ +#define CMS_MATCH_MODEL 2 /* Match down to model */ +#define CMS_MATCH_STEPPING 3 /* Match down to stepping */ + +/* + * Structure used to keep track of modules we have loaded. + */ +typedef struct cms { + struct cms *cms_next; + struct cms *cms_prev; + const cms_ops_t *cms_ops; + struct modctl *cms_modp; + uint_t cms_refcnt; +} cms_t; + +static cms_t *cms_list; +static kmutex_t cms_load_lock; + +/* + * We stash a cms_t and associated private data via cmi_hdl_setspecific. + */ +struct cms_ctl { + cms_t *cs_cms; + void *cs_cmsdata; +}; + +static cms_t * +cms_hdl_getcms(cmi_hdl_t hdl) +{ + struct cms_ctl *cdp = cmi_hdl_getspecific(hdl); + + return (cdp != NULL ? cdp->cs_cms : NULL); +} + +void * +cms_hdl_getcmsdata(cmi_hdl_t hdl) +{ + struct cms_ctl *cdp = cmi_hdl_getspecific(hdl); + + return (cdp != NULL ? cdp->cs_cmsdata : NULL); +} + +static void +cms_link(cms_t *cms) +{ + ASSERT(MUTEX_HELD(&cms_load_lock)); + + cms->cms_prev = NULL; + cms->cms_next = cms_list; + if (cms_list != NULL) + cms_list->cms_prev = cms; + cms_list = cms; +} + +static void +cms_unlink(cms_t *cms) +{ + ASSERT(MUTEX_HELD(&cms_load_lock)); + ASSERT(cms->cms_refcnt == 0); + + if (cms->cms_prev != NULL) + cms->cms_prev->cms_next = cms->cms_next; + + if (cms->cms_next != NULL) + cms->cms_next->cms_prev = cms->cms_prev; + + if (cms_list == cms) + cms_list = cms->cms_next; +} + +/* + * 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. + */ +static void +cms_hold(cms_t *cms) +{ + ASSERT(MUTEX_HELD(&cms_load_lock)); + + mutex_enter(&mod_lock); + cms->cms_modp->mod_ref++; + mutex_exit(&mod_lock); + cms->cms_refcnt++; +} + +static void +cms_rele(cms_t *cms) +{ + ASSERT(MUTEX_HELD(&cms_load_lock)); + + mutex_enter(&mod_lock); + cms->cms_modp->mod_ref--; + mutex_exit(&mod_lock); + + if (--cms->cms_refcnt == 0) { + cms_unlink(cms); + kmem_free(cms, sizeof (cms_t)); + } +} + +static cms_ops_t * +cms_getops(modctl_t *modp) +{ + cms_ops_t *ops; + + if ((ops = (cms_ops_t *)modlookup_by_modctl(modp, "_cms_ops")) == + NULL) { + cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no _cms_ops " + "found", modp->mod_modname); + return (NULL); + } + + if (ops->cms_init == NULL) { + cmn_err(CE_WARN, "cpu_ms module '%s' is invalid: no cms_init " + "entry point", modp->mod_modname); + return (NULL); + } + + return (ops); +} + +static cms_t * +cms_load_modctl(modctl_t *modp) +{ + cms_ops_t *ops; + uintptr_t ver; + cms_t *cms; + cms_api_ver_t apiver; + + ASSERT(MUTEX_HELD(&cms_load_lock)); + + for (cms = cms_list; cms != NULL; cms = cms->cms_next) { + if (cms->cms_modp == modp) + return (cms); + } + + if ((ver = modlookup_by_modctl(modp, "_cms_api_version")) == NULL) { + cmn_err(CE_WARN, "cpu model-specific module '%s' is invalid: " + "no _cms_api_version", modp->mod_modname); + return (NULL); + } else { + apiver = *((cms_api_ver_t *)ver); + if (!CMS_API_VERSION_CHKMAGIC(apiver)) { + cmn_err(CE_WARN, "cpu model-specific module '%s' is " + "invalid: _cms_api_version 0x%x has bad magic", + modp->mod_modname, apiver); + return (NULL); + } + } + + if (apiver != CMS_API_VERSION) { + cmn_err(CE_WARN, "cpu model-specific module '%s' has API " + "version %d, kernel requires API version %d", + modp->mod_modname, CMS_API_VERSION_TOPRINT(apiver), + CMS_API_VERSION_TOPRINT(CMS_API_VERSION)); + return (NULL); + } + + if ((ops = cms_getops(modp)) == NULL) + return (NULL); + + cms = kmem_zalloc(sizeof (cms_t), KM_SLEEP); + cms->cms_ops = ops; + cms->cms_modp = modp; + + cms_link(cms); + + return (cms); +} + +static int +cms_cpu_match(cmi_hdl_t hdl1, cmi_hdl_t hdl2, int match) +{ + if (match >= CMS_MATCH_VENDOR && + cmi_hdl_vendor(hdl1) != cmi_hdl_vendor(hdl2)) + return (0); + + if (match >= CMS_MATCH_FAMILY && + cmi_hdl_family(hdl1) != cmi_hdl_family(hdl2)) + return (0); + + if (match >= CMS_MATCH_MODEL && + cmi_hdl_model(hdl1) != cmi_hdl_model(hdl2)) + return (0); + + if (match >= CMS_MATCH_STEPPING && + cmi_hdl_stepping(hdl1) != cmi_hdl_stepping(hdl2)) + return (0); + + return (1); +} + +static int +cms_search_list_cb(cmi_hdl_t whdl, void *arg1, void *arg2, void *arg3) +{ + cmi_hdl_t thdl = (cmi_hdl_t)arg1; + int match = *((int *)arg2); + cmi_hdl_t *rsltp = (cmi_hdl_t *)arg3; + + if (cms_cpu_match(thdl, whdl, match)) { + cmi_hdl_hold(whdl); /* short-term hold */ + *rsltp = whdl; + return (CMI_HDL_WALK_DONE); + } else { + return (CMI_HDL_WALK_NEXT); + } +} + +/* + * 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. + */ +static cms_t * +cms_search_list(cmi_hdl_t hdl, int match) +{ + cmi_hdl_t dhdl = NULL; + cms_t *cms = NULL; + + ASSERT(MUTEX_HELD(&cms_load_lock)); + + cmi_hdl_walk(cms_search_list_cb, (void *)hdl, (void *)&match, &dhdl); + if (dhdl) { + cms = HDL2CMS(dhdl); + cmi_hdl_rele(dhdl); /* held in cms_search_list_cb */ + } + + return (cms); +} + +/* + * Try to find or load a module that offers model-specific support for + * this vendor/family/model/stepping combination. When attempting to load + * a module we look in CPUMOD_MS_SUBDIR first for a match on + * vendor/family/model/stepping, then on vendor/family/model (ignoring + * stepping), then on vendor/family (ignoring model and stepping), then + * on vendor alone. + */ +static cms_t * +cms_load_module(cmi_hdl_t hdl, int match, int *chosenp) +{ + modctl_t *modp; + cms_t *cms; + int modid; + uint_t s[3]; + + ASSERT(MUTEX_HELD(&cms_load_lock)); + ASSERT(match == CMS_MATCH_STEPPING || match == CMS_MATCH_MODEL || + match == CMS_MATCH_FAMILY || match == CMS_MATCH_VENDOR); + + s[0] = cmi_hdl_family(hdl); + s[1] = cmi_hdl_model(hdl); + s[2] = cmi_hdl_stepping(hdl); + + /* + * Have we already loaded a module for a cpu with the same + * vendor/family/model/stepping? + */ + if ((cms = cms_search_list(hdl, match)) != NULL) { + cms_hold(cms); + return (cms); + } + + modid = modload_qualified(CPUMOD_MS_SUBDIR, CPUMOD_MS_PREFIX, + cmi_hdl_vendorstr(hdl), ".", s, match, chosenp); + + if (modid == -1) + return (NULL); + + modp = mod_hold_by_id(modid); + cms = cms_load_modctl(modp); + if (cms) + cms_hold(cms); + mod_release_mod(modp); + + return (cms); +} + +static cms_t * +cms_load_specific(cmi_hdl_t hdl, void **datap) +{ + cms_t *cms; + int err; + int i; + + ASSERT(MUTEX_HELD(&cms_load_lock)); + + for (i = CMS_MATCH_STEPPING; i >= CMS_MATCH_VENDOR; i--) { + int suffixlevel; + + if ((cms = cms_load_module(hdl, i, &suffixlevel)) == NULL) + return (NULL); + + /* + * A module has loaded and has a _cms_ops structure, and the + * module has been held for this instance. Call the cms_init + * entry point - we expect success (0) or ENOTSUP. + */ + if ((err = cms->cms_ops->cms_init(hdl, datap)) == 0) { + if (boothowto & RB_VERBOSE) { + printf("initialized model-specific " + "module '%s' on chip %d core %d " + "strand %d\n", + cms->cms_modp->mod_modname, + cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), + cmi_hdl_strandid(hdl)); + } + return (cms); + } else if (err != ENOTSUP) { + cmn_err(CE_WARN, "failed to init model-specific " + "module '%s' on chip %d core %d strand %d: err=%d", + cms->cms_modp->mod_modname, + cmi_hdl_chipid(hdl), cmi_hdl_coreid(hdl), + cmi_hdl_strandid(hdl), err); + } + + /* + * The module failed or declined to init, so release + * it and potentially change i to be equal to he number + * of suffices actually used in the last module path. + */ + cms_rele(cms); + i = suffixlevel; + } + + return (NULL); +} + +void +cms_init(cmi_hdl_t hdl) +{ + cms_t *cms; + void *data; + + if (cms_no_model_specific != 0) + return; + + mutex_enter(&cms_load_lock); + + if ((cms = cms_load_specific(hdl, &data)) != NULL) { + struct cms_ctl *cdp; + + ASSERT(cmi_hdl_getspecific(hdl) == NULL); + + cdp = kmem_alloc(sizeof (*cdp), KM_SLEEP); + cdp->cs_cms = cms; + cdp->cs_cmsdata = data; + cmi_hdl_setspecific(hdl, cdp); + } + + mutex_exit(&cms_load_lock); +} + +void +cms_fini(cmi_hdl_t hdl) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_fini)) + CMS_OPS(cms)->cms_fini(hdl); +} + +boolean_t +cms_present(cmi_hdl_t hdl) +{ + return (HDL2CMS(hdl) != NULL ? B_TRUE : B_FALSE); +} + +void +cms_post_startup(cmi_hdl_t hdl) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_post_startup)) + CMS_OPS(cms)->cms_post_startup(hdl); +} + +void +cms_post_mpstartup(cmi_hdl_t hdl) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_post_mpstartup)) + CMS_OPS(cms)->cms_post_mpstartup(hdl); +} + +size_t +cms_logout_size(cmi_hdl_t hdl) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_logout_size)) + return (0); + + return (CMS_OPS(cms)->cms_logout_size(hdl)); +} + +uint64_t +cms_mcgctl_val(cmi_hdl_t hdl, int nbanks, uint64_t def) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_mcgctl_val)) + return (def); + + return (CMS_OPS(cms)->cms_mcgctl_val(hdl, nbanks, def)); +} + +boolean_t +cms_bankctl_skipinit(cmi_hdl_t hdl, int banknum) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_bankctl_skipinit)) + return (B_FALSE); + + return (CMS_OPS(cms)->cms_bankctl_skipinit(hdl, banknum)); +} + +uint64_t +cms_bankctl_val(cmi_hdl_t hdl, int banknum, uint64_t def) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_bankctl_val)) + return (def); + + return (CMS_OPS(cms)->cms_bankctl_val(hdl, banknum, def)); +} + +boolean_t +cms_bankstatus_skipinit(cmi_hdl_t hdl, int banknum) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_bankstatus_skipinit)) + return (B_FALSE); + + return (CMS_OPS(cms)->cms_bankstatus_skipinit(hdl, banknum)); +} + +uint64_t +cms_bankstatus_val(cmi_hdl_t hdl, int banknum, uint64_t def) +{ + cms_t *cms = HDL2CMS(hdl); + + if (!CMS_OP_PRESENT(cms, cms_bankstatus_val)) + return (def); + + return (CMS_OPS(cms)->cms_bankstatus_val(hdl, banknum, def)); +} + +void +cms_mca_init(cmi_hdl_t hdl, int nbanks) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_mca_init)) + CMS_OPS(cms)->cms_mca_init(hdl, nbanks); +} + +uint64_t +cms_poll_ownermask(cmi_hdl_t hdl, hrtime_t poll_interval) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_poll_ownermask)) + return (CMS_OPS(cms)->cms_poll_ownermask(hdl, poll_interval)); + else + return (-1ULL); /* poll all banks by default */ +} + +void +cms_bank_logout(cmi_hdl_t hdl, int banknum, uint64_t status, uint64_t addr, + uint64_t misc, void *mslogout) +{ + cms_t *cms = HDL2CMS(hdl); + + if (mslogout != NULL && CMS_OP_PRESENT(cms, cms_bank_logout)) + CMS_OPS(cms)->cms_bank_logout(hdl, banknum, status, addr, + misc, mslogout); +} + +cms_errno_t +cms_msrinject(cmi_hdl_t hdl, uint_t msr, uint64_t val) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_msrinject)) + return (CMS_OPS(cms)->cms_msrinject(hdl, msr, val)); + else + return (CMSERR_NOTSUP); +} + +uint32_t +cms_error_action(cmi_hdl_t hdl, int ismc, int banknum, uint64_t status, + uint64_t addr, uint64_t misc, void *mslogout) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_error_action)) + return (CMS_OPS(cms)->cms_error_action(hdl, ismc, banknum, + status, addr, misc, mslogout)); + else + return (0); +} + +cms_cookie_t +cms_disp_match(cmi_hdl_t hdl, int banknum, uint64_t status, uint64_t addr, + uint64_t misc, void *mslogout) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_disp_match)) + return (CMS_OPS(cms)->cms_disp_match(hdl, banknum, + status, addr, misc, mslogout)); + else + return (NULL); + +} + +void +cms_ereport_class(cmi_hdl_t hdl, cms_cookie_t mscookie, const char **cpuclsp, + const char **leafclsp) +{ + cms_t *cms = HDL2CMS(hdl); + + if (cpuclsp == NULL || leafclsp == NULL) + return; + + *cpuclsp = *leafclsp = NULL; + if (CMS_OP_PRESENT(cms, cms_ereport_class)) { + CMS_OPS(cms)->cms_ereport_class(hdl, mscookie, cpuclsp, + leafclsp); + } +} + +nvlist_t * +cms_ereport_detector(cmi_hdl_t hdl, cms_cookie_t mscookie, nv_alloc_t *nva) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_ereport_detector)) + return (CMS_OPS(cms)->cms_ereport_detector(hdl, mscookie, nva)); + else + return (NULL); + +} + +boolean_t +cms_ereport_includestack(cmi_hdl_t hdl, cms_cookie_t mscookie) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_ereport_includestack)) { + return (CMS_OPS(cms)->cms_ereport_includestack(hdl, mscookie)); + } else { + return (B_FALSE); + } +} + +void +cms_ereport_add_logout(cmi_hdl_t hdl, nvlist_t *nvl, nv_alloc_t *nva, + int banknum, uint64_t status, uint64_t addr, uint64_t misc, void *mslogout, + cms_cookie_t mscookie) +{ + cms_t *cms = HDL2CMS(hdl); + + if (CMS_OP_PRESENT(cms, cms_ereport_add_logout)) + CMS_OPS(cms)->cms_ereport_add_logout(hdl, nvl, nva, banknum, + status, addr, misc, mslogout, mscookie); + +} diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c index 04f73c9569..76a53e25ed 100644 --- a/usr/src/uts/i86pc/os/cpuid.c +++ b/usr/src/uts/i86pc/os/cpuid.c @@ -276,23 +276,37 @@ static struct cpuid_info cpuid_info0; * Second index by (model & 0x3) */ static uint32_t amd_skts[3][4] = { + /* + * Family 0xf revisions B through E + */ +#define A_SKTS_0 0 { X86_SOCKET_754, /* 0b00 */ X86_SOCKET_940, /* 0b01 */ X86_SOCKET_754, /* 0b10 */ X86_SOCKET_939 /* 0b11 */ }, + /* + * Family 0xf revisions F and G + */ +#define A_SKTS_1 1 { X86_SOCKET_S1g1, /* 0b00 */ X86_SOCKET_F1207, /* 0b01 */ X86_SOCKET_UNKNOWN, /* 0b10 */ X86_SOCKET_AM2 /* 0b11 */ }, + /* + * Family 0x10 revisions A and B + * It is not clear whether, as new sockets release, that + * model & 0x3 will id socket for this family + */ +#define A_SKTS_2 2 { X86_SOCKET_F1207, /* 0b00 */ X86_SOCKET_F1207, /* 0b01 */ X86_SOCKET_F1207, /* 0b10 */ - X86_SOCKET_F1207 /* 0b11 */ + X86_SOCKET_F1207, /* 0b11 */ } }; @@ -314,40 +328,56 @@ static const struct amd_rev_mapent { int rm_sktidx; } amd_revmap[] = { /* + * =============== AuthenticAMD Family 0xf =============== + */ + + /* * Rev B includes model 0x4 stepping 0 and model 0x5 stepping 0 and 1. */ - { 0xf, 0x04, 0x04, 0x0, 0x0, X86_CHIPREV_AMD_F_REV_B, "B", 0 }, - { 0xf, 0x05, 0x05, 0x0, 0x1, X86_CHIPREV_AMD_F_REV_B, "B", 0 }, + { 0xf, 0x04, 0x04, 0x0, 0x0, X86_CHIPREV_AMD_F_REV_B, "B", A_SKTS_0 }, + { 0xf, 0x05, 0x05, 0x0, 0x1, X86_CHIPREV_AMD_F_REV_B, "B", A_SKTS_0 }, /* * Rev C0 includes model 0x4 stepping 8 and model 0x5 stepping 8 */ - { 0xf, 0x04, 0x05, 0x8, 0x8, X86_CHIPREV_AMD_F_REV_C0, "C0", 0 }, + { 0xf, 0x04, 0x05, 0x8, 0x8, X86_CHIPREV_AMD_F_REV_C0, "C0", A_SKTS_0 }, /* * Rev CG is the rest of extended model 0x0 - i.e., everything * but the rev B and C0 combinations covered above. */ - { 0xf, 0x00, 0x0f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_CG, "CG", 0 }, + { 0xf, 0x00, 0x0f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_CG, "CG", A_SKTS_0 }, /* * Rev D has extended model 0x1. */ - { 0xf, 0x10, 0x1f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_D, "D", 0 }, + { 0xf, 0x10, 0x1f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_D, "D", A_SKTS_0 }, /* * Rev E has extended model 0x2. * Extended model 0x3 is unused but available to grow into. */ - { 0xf, 0x20, 0x3f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_E, "E", 0 }, + { 0xf, 0x20, 0x3f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_E, "E", A_SKTS_0 }, /* * Rev F has extended models 0x4 and 0x5. */ - { 0xf, 0x40, 0x5f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_F, "F", 1 }, + { 0xf, 0x40, 0x5f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_F, "F", A_SKTS_1 }, /* * Rev G has extended model 0x6. */ - { 0xf, 0x60, 0x6f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_G, "G", 1 }, + { 0xf, 0x60, 0x6f, 0x0, 0xf, X86_CHIPREV_AMD_F_REV_G, "G", A_SKTS_1 }, + + /* + * =============== AuthenticAMD Family 0x10 =============== + */ + + /* + * Rev A has model 0 and stepping 0/1/2 for DR-{A0,A1,A2}. + * Give all of model 0 stepping range to rev A. + */ + { 0x10, 0x00, 0x00, 0x0, 0x2, X86_CHIPREV_AMD_10_REV_A, "A", A_SKTS_2 }, + /* - * Family 0x10 Rev B has model 0x2. + * Rev B has model 2 and steppings 0/1/0xa/2 for DR-{B0,B1,BA,B2}. + * Give all of model 2 stepping range to rev B. */ - { 0x10, 0x02, 0x02, 0x0, 0xa, X86_CHIPREV_AMD_10_REV_B, "B", 2 } + { 0x10, 0x02, 0x02, 0x0, 0xf, X86_CHIPREV_AMD_10_REV_B, "B", A_SKTS_2 }, }; /* diff --git a/usr/src/uts/i86pc/os/mp_startup.c b/usr/src/uts/i86pc/os/mp_startup.c index b6556b7870..87f945811a 100644 --- a/usr/src/uts/i86pc/os/mp_startup.c +++ b/usr/src/uts/i86pc/os/mp_startup.c @@ -1494,17 +1494,24 @@ 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(); +#ifndef __xpv + { + /* + * 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_hdl_t hdl; - if (x86_feature & X86_MCA) - cmi_mca_init(); + if ((hdl = cmi_init(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), + cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) != NULL) { + if (x86_feature & X86_MCA) + cmi_mca_init(hdl); + } + } +#endif /* __xpv */ if (boothowto & RB_DEBUG) kdi_cpu_init(); @@ -1598,17 +1605,34 @@ cpu_enable_intr(struct cpu *cp) } - +/*ARGSUSED*/ void mp_cpu_faulted_enter(struct cpu *cp) { - cmi_faulted_enter(cp); +#ifndef __xpv + cmi_hdl_t hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(cp), + cmi_ntv_hwcoreid(cp), cmi_ntv_hwstrandid(cp)); + + if (hdl != NULL) { + cmi_faulted_enter(hdl); + cmi_hdl_rele(hdl); + } +#endif } +/*ARGSUSED*/ void mp_cpu_faulted_exit(struct cpu *cp) { - cmi_faulted_exit(cp); +#ifndef __xpv + cmi_hdl_t hdl = cmi_hdl_lookup(CMI_HDL_NATIVE, cmi_ntv_hwchipid(cp), + cmi_ntv_hwcoreid(cp), cmi_ntv_hwstrandid(cp)); + + if (hdl != NULL) { + cmi_faulted_exit(hdl); + cmi_hdl_rele(hdl); + } +#endif } /* diff --git a/usr/src/uts/i86pc/os/startup.c b/usr/src/uts/i86pc/os/startup.c index 28cb6c4830..dfddf8e8e2 100644 --- a/usr/src/uts/i86pc/os/startup.c +++ b/usr/src/uts/i86pc/os/startup.c @@ -1428,17 +1428,22 @@ startup_modules(void) */ setup_ddi(); - /* - * Set up the CPU module subsystem. Modifies the device tree, so it - * must be done after setup_ddi(). - */ - cmi_init(); +#ifndef __xpv + { + /* + * Set up the CPU module subsystem. Modifies the device tree, + * so it must be done after setup_ddi(). + */ - /* - * Initialize the MCA handlers - */ - if (x86_feature & X86_MCA) - cmi_mca_init(); + cmi_hdl_t hdl; + + if ((hdl = cmi_init(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), + cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) != NULL) { + if (x86_feature & X86_MCA) + cmi_mca_init(hdl); + } + } +#endif /* __xpv */ /* * Fake a prom tree such that /dev/openprom continues to work @@ -2041,7 +2046,7 @@ post_startup(void) /* * Complete CPU module initialization */ - cmi_post_init(); + cmi_post_startup(); /* * Perform forceloading tasks for /etc/system. diff --git a/usr/src/uts/i86pc/os/trap.c b/usr/src/uts/i86pc/os/trap.c index 5316babf1a..2afb25e3c7 100644 --- a/usr/src/uts/i86pc/os/trap.c +++ b/usr/src/uts/i86pc/os/trap.c @@ -99,6 +99,7 @@ #if defined(__xpv) #include <sys/hypervisor.h> #endif +#include <sys/contract/process_impl.h> #define USER 0x10000 /* user-mode flag added to trap type */ @@ -580,12 +581,14 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) case T_PGFLT: /* system page fault */ /* * If we're under on_trap() protection (see <sys/ontrap.h>), - * set ot_trap and longjmp back to the on_trap() call site. + * set ot_trap and bounce back to the on_trap() call site + * via the installed trampoline. */ if ((ct->t_ontrap != NULL) && (ct->t_ontrap->ot_prot & OT_DATA_ACCESS)) { ct->t_ontrap->ot_trap |= OT_DATA_ACCESS; - longjmp(&curthread->t_ontrap->ot_jmpbuf); + rp->r_pc = ct->t_ontrap->ot_trampoline; + goto cleanup; } /* @@ -1098,7 +1101,7 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) /* * If we're under on_trap() protection (see <sys/ontrap.h>), - * set ot_trap and longjmp back to the on_trap() call site + * set ot_trap and trampoline back to the on_trap() call site * for OT_DATA_ACCESS or OT_SEGMENT_ACCESS. */ if (ct->t_ontrap != NULL) { @@ -1109,7 +1112,8 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) ct->t_ontrap->ot_trap |= ttype; if (tudebug) showregs(type, rp, (caddr_t)0); - longjmp(&curthread->t_ontrap->ot_jmpbuf); + rp->r_pc = ct->t_ontrap->ot_trampoline; + goto cleanup; } } @@ -1144,7 +1148,8 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) ct->t_ontrap->ot_trap |= OT_SEGMENT_ACCESS; if (tudebug) showregs(type, rp, (caddr_t)0); - longjmp(&curthread->t_ontrap->ot_jmpbuf); + rp->r_pc = ct->t_ontrap->ot_trampoline; + goto cleanup; } #endif /* __amd64 */ /*FALLTHROUGH*/ @@ -1305,8 +1310,15 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) */ goto cleanup; - case T_AST + USER: /* profiling or resched pseudo trap */ - if (lwp->lwp_pcb.pcb_flags & CPC_OVERFLOW) { + case T_AST + USER: /* profiling, resched, h/w error pseudo trap */ + if (lwp->lwp_pcb.pcb_flags & ASYNC_HWERR) { + proc_t *p = ttoproc(curthread); + + lwp->lwp_pcb.pcb_flags &= ~ASYNC_HWERR; + contract_process_hwerr(p->p_ct_process, p); + siginfo.si_signo = SIGKILL; + siginfo.si_code = SI_NOINFO; + } else if (lwp->lwp_pcb.pcb_flags & CPC_OVERFLOW) { lwp->lwp_pcb.pcb_flags &= ~CPC_OVERFLOW; if (kcpc_overflow_ast()) { /* @@ -1321,6 +1333,7 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid) fault = FLTCPCOVF; } } + break; } diff --git a/usr/src/uts/i86pc/sys/cpu_module.h b/usr/src/uts/i86pc/sys/cpu_module.h index 1ae4e149cc..9f5d7d2e85 100644 --- a/usr/src/uts/i86pc/sys/cpu_module.h +++ b/usr/src/uts/i86pc/sys/cpu_module.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,35 +33,170 @@ #include <sys/cpuvar.h> #include <sys/nvpair.h> #include <sys/mc.h> +#include <sys/sunddi.h> #ifdef __cplusplus extern "C" { #endif +#ifdef _KERNEL + +#define CMIERR_BASE 0xc000 + +typedef enum cmi_errno { + CMI_SUCCESS = 0, + /* + * CPU Module Interface API error return values/ + */ + CMIERR_UNKNOWN = CMIERR_BASE, /* no specific error reason reported */ + CMIERR_API, /* API usage error caught */ + CMIERR_NOTSUP, /* Unsupported operation */ + CMIERR_HDL_CLASS, /* Inappropriate handle class */ + CMIERR_HDL_NOTFOUND, /* Can't find handle for resource */ + CMIERR_MSRGPF, /* #GP during cmi_hdl_{wr,rd}msr */ + CMIERR_INTERPOSE, /* MSR/PCICFG interposition error */ + CMIERR_DEADLOCK, /* Deadlock avoidance */ + /* + * Memory-controller related errors + */ + CMIERR_MC_ABSENT, /* No, or not yet registered, MC ops */ + CMIERR_MC_NOTSUP, /* Requested functionality unimpld */ + CMIERR_MC_NOMEMSCRUB, /* No dram scrubber, or disabled */ + CMIERR_MC_SYNDROME, /* Invalid syndrome or syndrome type */ + CMIERR_MC_BADSTATE, /* MC driver state is invalid */ + CMIERR_MC_NOADDR, /* Address not found */ + CMIERR_MC_RSRCNOTPRESENT, /* Resource not present in system */ + CMIERR_MC_ADDRBITS, /* Too few valid addr bits */ + CMIERR_MC_INVALUNUM, /* Invalid input unum */ + CMIERR_MC_PARTIALUNUMTOPA /* unum to pa reflected physaddr */ +} cmi_errno_t; + + +/* + * All access to cpu information is made via a handle, in order to get + * the desired info even when running non-natively. + * + * A CMI_HDL_NATIVE handle is used when we believe we are running on + * bare-metal. If we *are* on bare metal then this handle type will + * get us through to the real hardware, and there will be a 1:1 correspondence + * between handles and cpu_t structures; if not, say we are a domU to + * some unknown/undetected/unannounced hypervisor then chances are the + * hypervisor is not exposing much hardware detail to us so we should + * be prepared for some operations that "cannot fail" to fail or return + * odd data. + * + * (Once implemented) A CMI_HDL_SOLARIS_xVM_MCA is used when we are running + * in i86xpv architecture - dom0 to a Solaris xVM hypervisor - and want to + * use a handle on each real execution core (as opposed to vcpu) + * to perform MCA related activities. The model for this handle type + * is that the hypervisor continues to own the real hardware and + * includes a polling service and #MC handler which forward error + * telemetry to dom0 for logging and diagnosis. As such, the operations + * such as RDMSR and WRMSR for this handle type do *not* read and write + * real MSRs via hypercalls- instead they should provide the values from + * already-read MCA bank telemetry, and writes are discarded. + * + * If some application requires real MSR read and write access another + * handle class should be introduced. + */ + +typedef struct cmi_hdl *cmi_hdl_t; /* opaque chip/core/strand handle */ + +enum cmi_hdl_class { + CMI_HDL_NATIVE, + CMI_HDL_SOLARIS_xVM_MCA /* not implemented yet */ +}; + struct regs; -struct cmi_mc_ops; +struct cmi_mc_ops; /* defined in cpu_module_impl.h */ + +extern cmi_hdl_t cmi_init(enum cmi_hdl_class, uint_t, uint_t, uint_t); +extern void cmi_post_startup(void); +extern void cmi_post_mpstartup(void); +extern void cmi_fini(cmi_hdl_t); + +extern void cmi_hdl_hold(cmi_hdl_t); +extern void cmi_hdl_rele(cmi_hdl_t); +extern void *cmi_hdl_getcmidata(cmi_hdl_t); +extern void cmi_hdl_setspecific(cmi_hdl_t, void *); +extern void *cmi_hdl_getspecific(cmi_hdl_t); +extern const struct cmi_mc_ops *cmi_hdl_getmcops(cmi_hdl_t); +extern void *cmi_hdl_getmcdata(cmi_hdl_t); +extern enum cmi_hdl_class cmi_hdl_class(cmi_hdl_t); +extern cmi_hdl_t cmi_hdl_lookup(enum cmi_hdl_class, uint_t, uint_t, uint_t); +extern cmi_hdl_t cmi_hdl_any(void); + +#define CMI_HDL_WALK_NEXT 0 +#define CMI_HDL_WALK_DONE 1 +extern void cmi_hdl_walk(int (*)(cmi_hdl_t, void *, void *, void *), + void *, void *, void *); + +extern void cmi_hdlconf_rdmsr_nohw(cmi_hdl_t); +extern void cmi_hdlconf_wrmsr_nohw(cmi_hdl_t); +extern cmi_errno_t cmi_hdl_rdmsr(cmi_hdl_t, uint_t, uint64_t *); +extern cmi_errno_t cmi_hdl_wrmsr(cmi_hdl_t, uint_t, uint64_t); + +extern void cmi_hdl_enable_mce(cmi_hdl_t); + +extern uint_t cmi_hdl_vendor(cmi_hdl_t); +extern const char *cmi_hdl_vendorstr(cmi_hdl_t); +extern uint_t cmi_hdl_family(cmi_hdl_t); +extern uint_t cmi_hdl_model(cmi_hdl_t); +extern uint_t cmi_hdl_stepping(cmi_hdl_t); +extern uint_t cmi_hdl_chipid(cmi_hdl_t); +extern uint_t cmi_hdl_dieid(cmi_hdl_t); +extern uint_t cmi_hdl_coreid(cmi_hdl_t); +extern uint_t cmi_hdl_strandid(cmi_hdl_t); +extern uint32_t cmi_hdl_chiprev(cmi_hdl_t); +extern const char *cmi_hdl_chiprevstr(cmi_hdl_t); +extern uint32_t cmi_hdl_getsockettype(cmi_hdl_t); + +#ifndef __xpv +extern uint_t cmi_ntv_hwchipid(cpu_t *); +extern uint_t cmi_ntv_hwcoreid(cpu_t *); +extern uint_t cmi_ntv_hwstrandid(cpu_t *); +#endif /* __xpv */ 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_post_mpstartup(void); +extern cmi_errno_t cmi_hdl_msrinject(cmi_hdl_t, cmi_mca_regs_t *, uint_t, + int); +extern void cmi_hdl_msrinterpose(cmi_hdl_t, cmi_mca_regs_t *, uint_t); + +extern void cmi_faulted_enter(cmi_hdl_t); +extern void cmi_faulted_exit(cmi_hdl_t); + +extern void cmi_pcird_nohw(void); +extern void cmi_pciwr_nohw(void); +extern uint8_t cmi_pci_getb(int, int, int, int, int *, ddi_acc_handle_t); +extern uint16_t cmi_pci_getw(int, int, int, int, int *, ddi_acc_handle_t); +extern uint32_t cmi_pci_getl(int, int, int, int, int *, ddi_acc_handle_t); +extern void cmi_pci_interposeb(int, int, int, int, uint8_t); +extern void cmi_pci_interposew(int, int, int, int, uint16_t); +extern void cmi_pci_interposel(int, int, int, int, uint32_t); +extern void cmi_pci_putb(int, int, int, int, ddi_acc_handle_t, uint8_t); +extern void cmi_pci_putw(int, int, int, int, ddi_acc_handle_t, uint16_t); +extern void cmi_pci_putl(int, int, int, int, ddi_acc_handle_t, uint32_t); -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, int); +extern void cmi_mca_init(cmi_hdl_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_hdl_poke(cmi_hdl_t); +extern void cmi_hdl_mcheck(cmi_hdl_t); -extern void cmi_mc_register(struct cpu *, const struct cmi_mc_ops *, void *); -extern int cmi_mc_patounum(uint64_t, uint8_t, uint8_t, uint32_t, int, +extern void cmi_mca_trap(struct regs *); + +extern boolean_t cmi_panic_on_ue(void); + +extern void cmi_mc_register(cmi_hdl_t, const struct cmi_mc_ops *, void *); +extern cmi_errno_t cmi_mc_patounum(uint64_t, uint8_t, uint8_t, uint32_t, int, mc_unum_t *); -extern int cmi_mc_unumtopa(mc_unum_t *, nvlist_t *, uint64_t *); +extern cmi_errno_t cmi_mc_unumtopa(mc_unum_t *, nvlist_t *, uint64_t *); +extern void cmi_mc_logout(cmi_hdl_t, boolean_t, boolean_t); + +#endif /* _KERNEL */ #ifdef __cplusplus } diff --git a/usr/src/uts/i86pc/sys/cpu_module_impl.h b/usr/src/uts/i86pc/sys/cpu_module_impl.h index 5cec3131db..c072a2566b 100644 --- a/usr/src/uts/i86pc/sys/cpu_module_impl.h +++ b/usr/src/uts/i86pc/sys/cpu_module_impl.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,36 +37,63 @@ extern "C" { #endif +typedef uint32_t cmi_api_ver_t; + +#define _CMI_API_VERSION_MAGIC 0xa5100000 +#define _CMI_API_VERSION(n) (_CMI_API_VERSION_MAGIC | (n)) + +#define CMI_API_VERSION_CHKMAGIC(v) \ + (((v) & 0xfff00000) == _CMI_API_VERSION_MAGIC) +#define CMI_API_VERSION_TOPRINT(v) ((v) & 0x000fffff) + +#define CMI_API_VERSION_0 _CMI_API_VERSION(0) +#define CMI_API_VERSION_1 _CMI_API_VERSION(1) + +#define CMI_API_VERSION CMI_API_VERSION_1 + typedef struct cmi_mc_ops { - int (*cmi_mc_patounum)(void *, uint64_t, uint8_t, uint8_t, uint32_t, - int, mc_unum_t *); - int (*cmi_mc_unumtopa)(void *, mc_unum_t *, nvlist_t *, uint64_t *); + cmi_errno_t (*cmi_mc_patounum)(void *, uint64_t, uint8_t, uint8_t, + uint32_t, int, mc_unum_t *); + cmi_errno_t (*cmi_mc_unumtopa)(void *, mc_unum_t *, nvlist_t *, + uint64_t *); + void (*cmi_mc_logout)(cmi_hdl_t, boolean_t, boolean_t); } cmi_mc_ops_t; typedef struct cmi_ops { - int (*cmi_init)(cpu_t *, void **); - void (*cmi_post_init)(void *); - void (*cmi_post_mpstartup)(void *); - void (*cmi_fini)(void *); - void (*cmi_faulted_enter)(void *); - void (*cmi_faulted_exit)(void *); - int (*cmi_scrubber_enable)(void *, uint64_t, uint64_t, int); - 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 *); + int (*cmi_init)(cmi_hdl_t, void **); + void (*cmi_post_startup)(cmi_hdl_t); + void (*cmi_post_mpstartup)(cmi_hdl_t); + void (*cmi_faulted_enter)(cmi_hdl_t); + void (*cmi_faulted_exit)(cmi_hdl_t); + void (*cmi_mca_init)(cmi_hdl_t); + uint64_t (*cmi_mca_trap)(cmi_hdl_t, struct regs *); + cmi_errno_t (*cmi_msrinject)(cmi_hdl_t, cmi_mca_regs_t *, uint_t, int); + void (*cmi_hdl_poke)(cmi_hdl_t); + void (*cmi_fini)(cmi_hdl_t); } 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; +/* + * Utility functions provided by the cpu module interface for the sole + * use of cpu module implementations. + */ +extern int cmi_mce_response(struct regs *, uint64_t); + +/* + * Terminal dispositions to be returned by cmi_mca_trap entry point + */ +#define CMI_ERRDISP_CURCTXBAD 0x00000001ULL +#define CMI_ERRDISP_RIPV_INVALID 0x00000002ULL +#define CMI_ERRDISP_UC_UNCONSTRAINED 0x00000004ULL +#define CMI_ERRDISP_FORCEFATAL 0x00000008ULL -extern int cmi_panic_on_uncorrectable_error; +/* + * Non-terminal errors dispositions that can be returned by cmi_mca_trap + */ +#define CMI_ERRDISP_IGNORED 0x00010000ULL +#define CMI_ERRDISP_PCC_CLEARED 0x00020000ULL +#define CMI_ERRDISP_UC_CLEARED 0x00040000ULL +#define CMI_ERRDISP_POISONED 0x00080000ULL +#define CMI_ERRDISP_INCONSISTENT 0x00100000ULL #ifdef __cplusplus } diff --git a/usr/src/uts/i86pc/sys/cpu_module_ms.h b/usr/src/uts/i86pc/sys/cpu_module_ms.h new file mode 100644 index 0000000000..f997fbba97 --- /dev/null +++ b/usr/src/uts/i86pc/sys/cpu_module_ms.h @@ -0,0 +1,130 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _CPU_MODULE_MS_H +#define _CPU_MODULE_MS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/cpuvar.h> +#include <sys/nvpair.h> +#include <sys/cpu_module.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL + +#define CMSERR_BASE 0xe000 + +typedef enum cms_errno { + CMS_SUCCESS = 0, + CMSERR_UNKNOWN = CMSERR_BASE, /* No specific error reason given */ + CMSERR_NOTSUP, /* Unsupported operation */ + CMSERR_BADMSRWRITE /* Error on wrmsr */ + +} cms_errno_t; + +extern void cms_init(cmi_hdl_t); +extern boolean_t cms_present(cmi_hdl_t); +extern void *cms_hdl_getcmsdata(cmi_hdl_t); +extern void cms_post_startup(cmi_hdl_t); +extern void cms_post_mpstartup(cmi_hdl_t); + +extern size_t cms_logout_size(cmi_hdl_t); + +extern uint64_t cms_mcgctl_val(cmi_hdl_t, int, uint64_t); + +extern boolean_t cms_bankctl_skipinit(cmi_hdl_t, int); +extern uint64_t cms_bankctl_val(cmi_hdl_t, int, uint64_t); +extern boolean_t cms_bankstatus_skipinit(cmi_hdl_t, int); +extern uint64_t cms_bankstatus_val(cmi_hdl_t, int, uint64_t); + +extern void cms_mca_init(cmi_hdl_t, int); + +extern uint64_t cms_poll_ownermask(cmi_hdl_t, hrtime_t); + +extern void cms_bank_logout(cmi_hdl_t, int, uint64_t, uint64_t, uint64_t, + void *); + +extern cms_errno_t cms_msrinject(cmi_hdl_t, uint_t, uint64_t); + +extern void cms_fini(cmi_hdl_t); + +/* + * Return flags for cms_error_action. The model-specific implementation + * can perform additional error handling during this call (e.g., cache + * flush). but it needs to return an indication to the caller as to + * the high-level impact of the error. + * + * CMS_ERRSCOPE_CLEAREDUC indicates that a UC error has in some way + * been cleared by the model-specific handling, and that no bad data + * remains in the system as far as this error is concerned. + * + * CMS_ERRSCOPE_POISONED indicates that the uncorrected data has + * been marked in some way to ensure that is cannot subsequently be mistaken + * for good data. + * + * CMS_ERRSCOPE_CURCONTEXT_OK indicates that the interrupted context is + * unaffected by the uncorrected error. + * + * CMS_ERRSCOPE_IGNORE_ERR indicates that the error should be ignored, + * regardless of apparent current context status and presence of uncorrected + * data. + * + * CMS_ERRSCOPE_FORCE_FATAL indicates that the error should be considered + * terminal, even if no uncorrected data is present and context appears ok + */ + +#define CMS_ERRSCOPE_CLEARED_UC 0x01 +#define CMS_ERRSCOPE_POISONED 0x02 +#define CMS_ERRSCOPE_CURCONTEXT_OK 0x04 +#define CMS_ERRSCOPE_IGNORE_ERR 0x08 +#define CMS_ERRSCOPE_FORCE_FATAL 0x10 + +typedef void *cms_cookie_t; + +extern uint32_t cms_error_action(cmi_hdl_t, int, int, uint64_t, uint64_t, + uint64_t, void *); + +extern cms_cookie_t cms_disp_match(cmi_hdl_t, int, uint64_t, uint64_t, uint64_t, + void *); +extern void cms_ereport_class(cmi_hdl_t, cms_cookie_t, const char **, + const char **); +extern nvlist_t *cms_ereport_detector(cmi_hdl_t, cms_cookie_t, nv_alloc_t *); +extern boolean_t cms_ereport_includestack(cmi_hdl_t, cms_cookie_t); +extern void cms_ereport_add_logout(cmi_hdl_t, nvlist_t *, nv_alloc_t *, int, + uint64_t, uint64_t, uint64_t, void *, cms_cookie_t); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _CPU_MODULE_MS_H */ diff --git a/usr/src/uts/i86pc/sys/cpu_module_ms_impl.h b/usr/src/uts/i86pc/sys/cpu_module_ms_impl.h new file mode 100644 index 0000000000..1a545bcf76 --- /dev/null +++ b/usr/src/uts/i86pc/sys/cpu_module_ms_impl.h @@ -0,0 +1,88 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _CPU_MODULE_MS_IMPL_H +#define _CPU_MODULE_MS_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/cpu_module_ms.h> +#include <sys/cpuvar.h> +#include <sys/x86_archext.h> +#include <sys/nvpair.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t cms_api_ver_t; + +#define _CMS_API_VERSION_MAGIC 0xc5500000 +#define _CMS_API_VERSION(n) (_CMS_API_VERSION_MAGIC | (n)) + +#define CMS_API_VERSION_CHKMAGIC(v) \ + (((v) & 0xfff00000) == _CMS_API_VERSION_MAGIC) +#define CMS_API_VERSION_TOPRINT(v) ((v) & 0x000fffff) + +#define CMS_API_VERSION_0 _CMS_API_VERSION(0) + +#define CMS_API_VERSION CMS_API_VERSION_0 + +typedef struct cms_ops { + int (*cms_init)(cmi_hdl_t, void **); + void (*cms_post_startup)(cmi_hdl_t); + void (*cms_post_mpstartup)(cmi_hdl_t); + size_t (*cms_logout_size)(cmi_hdl_t); + uint64_t (*cms_mcgctl_val)(cmi_hdl_t, int, uint64_t); + boolean_t (*cms_bankctl_skipinit)(cmi_hdl_t, int); + uint64_t (*cms_bankctl_val)(cmi_hdl_t, int, uint64_t); + boolean_t (*cms_bankstatus_skipinit)(cmi_hdl_t, int); + uint64_t (*cms_bankstatus_val)(cmi_hdl_t, int, uint64_t); + void (*cms_mca_init)(cmi_hdl_t, int); + uint64_t (*cms_poll_ownermask)(cmi_hdl_t, hrtime_t); + void (*cms_bank_logout)(cmi_hdl_t, int, uint64_t, + uint64_t, uint64_t, void *); + uint32_t (*cms_error_action)(cmi_hdl_t, int, int, uint64_t, + uint64_t, uint64_t, void *); + cms_cookie_t (*cms_disp_match)(cmi_hdl_t, int, uint64_t, uint64_t, + uint64_t, void *); + void (*cms_ereport_class)(cmi_hdl_t, cms_cookie_t, const char **, + const char **); + nvlist_t *(*cms_ereport_detector)(cmi_hdl_t, cms_cookie_t, + nv_alloc_t *); + boolean_t (*cms_ereport_includestack)(cmi_hdl_t, cms_cookie_t); + void (*cms_ereport_add_logout)(cmi_hdl_t, nvlist_t *, + nv_alloc_t *, int, uint64_t, uint64_t, uint64_t, void *, + cms_cookie_t); + cms_errno_t (*cms_msrinject)(cmi_hdl_t, uint_t, uint64_t); + void (*cms_fini)(cmi_hdl_t); +} cms_ops_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _CPU_MODULE_MS_IMPL_H */ diff --git a/usr/src/uts/i86pc/sys/machcpuvar.h b/usr/src/uts/i86pc/sys/machcpuvar.h index f286c8936d..7a65366138 100644 --- a/usr/src/uts/i86pc/sys/machcpuvar.h +++ b/usr/src/uts/i86pc/sys/machcpuvar.h @@ -51,7 +51,6 @@ extern "C" { typedef void *cpu_pri_lev_t; struct cpuid_info; -struct cmi; struct cpu_ucode_info; /* @@ -104,8 +103,6 @@ struct machcpu { 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/i86xpv/Makefile.files b/usr/src/uts/i86xpv/Makefile.files index 89f62d2ee6..e0393e231c 100644 --- a/usr/src/uts/i86xpv/Makefile.files +++ b/usr/src/uts/i86xpv/Makefile.files @@ -38,6 +38,8 @@ CORE_OBJS += \ biosdisk.o \ cbe.o \ cmi.o \ + cmi_hw.o \ + cms.o \ confunix.o \ cpuid.o \ cpupm.o \ diff --git a/usr/src/uts/i86xpv/Makefile.i86xpv.shared b/usr/src/uts/i86xpv/Makefile.i86xpv.shared index bcbd949b17..1233a29df0 100644 --- a/usr/src/uts/i86xpv/Makefile.i86xpv.shared +++ b/usr/src/uts/i86xpv/Makefile.i86xpv.shared @@ -266,11 +266,6 @@ DRV_KMODS += evtchn DRV_KMODS += balloon # -# CPU Modules -# -CPU_KMODS += generic_cpu - -# # Exec Class Modules (/kernel/exec): # EXEC_KMODS += diff --git a/usr/src/uts/intel/io/pciex/pcie_error.c b/usr/src/uts/intel/io/pciex/pcie_error.c index 34d01ec6ee..c6781f4723 100644 --- a/usr/src/uts/intel/io/pciex/pcie_error.c +++ b/usr/src/uts/intel/io/pciex/pcie_error.c @@ -42,8 +42,10 @@ #include <sys/promif.h> #include <io/pciex/pcie_error.h> #include <io/pciex/pcie_nvidia.h> +#include <io/pciex/pcie_nb5000.h> extern uint32_t pcie_expected_ue_mask; +int pcie_intel_error_disable = 1; #ifdef DEBUG uint_t pcie_error_debug_flags = 0; @@ -149,9 +151,9 @@ static void pcie_check_io_mem_range(ddi_acc_handle_t, boolean_t *, boolean_t *); static uint16_t pcie_error_find_cap_reg(ddi_acc_handle_t, uint8_t); static uint16_t pcie_error_find_ext_aer_capid(ddi_acc_handle_t); -static void pcie_nvidia_error_init(dev_info_t *, ddi_acc_handle_t, - uint16_t, uint16_t); -static void pcie_nvidia_error_fini(ddi_acc_handle_t, uint16_t, uint16_t); +static void pcie_error_init(dev_info_t *, ddi_acc_handle_t, + uint16_t, uint16_t); +static void pcie_error_fini(ddi_acc_handle_t, uint16_t, uint16_t); /* * modload support @@ -184,6 +186,18 @@ _info(struct modinfo *modinfop) return (mod_info(&modlinkage, modinfop)); } +static int +pcie_error_supported(uint16_t vendor_id, uint16_t device_id) +{ + if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(device_id)) + return (1); + if ((vendor_id == INTEL_VENDOR_ID) && + INTEL_NB5000_PCIE_DEV_ID(device_id) && !pcie_intel_error_disable) + return (1); + + return (0); +} + /* * PCI-Express error initialization. */ @@ -324,14 +338,14 @@ pcie_error_enable(dev_info_t *cdip, ddi_acc_handle_t cfg_hdl) } /* - * For Nvidia chipset, call pcie_nvidia_error_init(). + * For Nvidia, Intel 5000 and 7300 chipset, call pcie_error_init(). * - * For non-Nvidia Root Ports, disable UR for all child devices by + * For other Root Ports, disable UR for all child devices by * changing the default ue mask (for AER devices) and the default * device control value (for non-AER device). */ - if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(device_id)) - pcie_nvidia_error_init(cdip, cfg_hdl, cap_ptr, aer_ptr); + if (pcie_error_supported(vendor_id, device_id)) + pcie_error_init(cdip, cfg_hdl, cap_ptr, aer_ptr); else if (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) { pcie_expected_ue_mask |= PCIE_AER_UCE_UR; pcie_device_ctrl_default &= ~PCIE_DEVCTL_UR_REPORTING_EN; @@ -437,18 +451,18 @@ pcie_check_io_mem_range(ddi_acc_handle_t cfg_hdl, boolean_t *empty_io_range, /* ARGSUSED */ static void -pcie_nvidia_error_init(dev_info_t *child, ddi_acc_handle_t cfg_hdl, +pcie_error_init(dev_info_t *child, ddi_acc_handle_t cfg_hdl, uint16_t cap_ptr, uint16_t aer_ptr) { uint16_t rc_ctl; /* Program SERR_FORWARD bit in NV_XVR_INTR_BCR */ - rc_ctl = pci_config_get16(cfg_hdl, NVIDIA_INTR_BCR_OFF + 0x2); - pci_config_put16(cfg_hdl, NVIDIA_INTR_BCR_OFF + 0x2, - rc_ctl | NVIDIA_INTR_BCR_SERR_FORWARD_BIT); + rc_ctl = pci_config_get16(cfg_hdl, PCI_BCNF_BCNTRL); + pci_config_put16(cfg_hdl, PCI_BCNF_BCNTRL, + rc_ctl | PCI_BCNF_BCNTRL_SERR_ENABLE); PCIE_ERROR_DBG("%s: PCIe NV_XVR_INTR_BCR=0x%x->0x%x\n", ddi_driver_name(child), rc_ctl, - pci_config_get16(cfg_hdl, NVIDIA_INTR_BCR_OFF + 0x2)); + pci_config_get16(cfg_hdl, PCI_BCNF_BCNTRL)); #if defined(__xpv) /* @@ -562,8 +576,8 @@ pcie_error_disable(dev_info_t *cdip, ddi_acc_handle_t cfg_hdl) /* * Only disable these set of errors for CK8-04/IO-4 devices */ - if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(device_id)) - pcie_nvidia_error_fini(cfg_hdl, cap_ptr, aer_ptr); + if (pcie_error_supported(vendor_id, device_id)) + pcie_error_fini(cfg_hdl, cap_ptr, aer_ptr); if (aer_ptr == PCIE_EXT_CAP_NEXT_PTR_NULL) return; @@ -591,7 +605,7 @@ pcie_error_disable(dev_info_t *cdip, ddi_acc_handle_t cfg_hdl) static void -pcie_nvidia_error_fini(ddi_acc_handle_t cfg_hdl, uint16_t cap_ptr, +pcie_error_fini(ddi_acc_handle_t cfg_hdl, uint16_t cap_ptr, uint16_t aer_ptr) { uint16_t rc_ctl; diff --git a/usr/src/uts/intel/io/pciex/pcie_nb5000.h b/usr/src/uts/intel/io/pciex/pcie_nb5000.h new file mode 100644 index 0000000000..2c4c286f75 --- /dev/null +++ b/usr/src/uts/intel/io/pciex/pcie_nb5000.h @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PCIEX_PCI_INTEL_NB5000_H +#define _PCIEX_PCI_INTEL_NB5000_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define INTEL_VENDOR_ID 0x8086 + +#define INTEL_NB5000_PCIE_DEV_ID(did) (((did) >= 0x3600 && (did) <= 0x360a) || \ + ((did) == 0x25d8 || (did) == 0x25d4 || (did) == 0x25c0 || \ + (did) == 0x25d0 || ((did) >= 0x25e2 && (did) <= 0x25e7)) || \ + ((did) >= 0x25f7 && (did) <= 0x25fa)) + +extern int pcie_intel_error_disable; + +#ifdef __cplusplus +} +#endif + +#endif /* _PCIEX_PCI_INTEL_NB5000_H */ diff --git a/usr/src/uts/intel/io/pciex/pcie_pci.c b/usr/src/uts/intel/io/pciex/pcie_pci.c index abf22ceb20..cf73c743be 100644 --- a/usr/src/uts/intel/io/pciex/pcie_pci.c +++ b/usr/src/uts/intel/io/pciex/pcie_pci.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -50,6 +50,7 @@ #include <sys/hotplug/pci/pciehpc.h> #include <io/pciex/pcie_error.h> #include <io/pciex/pcie_nvidia.h> +#include <io/pciex/pcie_nb5000.h> #ifdef DEBUG static int pepb_debug = 0; @@ -158,7 +159,7 @@ struct dev_ops pepb_ops = { static struct modldrv modldrv = { &mod_driverops, /* Type of module */ - "PCIe to PCI nexus driver %I%", + "PCIe to PCI nexus driver 1.10", &pepb_ops, /* driver ops */ }; @@ -247,7 +248,7 @@ static int pepb_pcie_port_type(dev_info_t *dip, static uint_t pepb_intx_intr(caddr_t arg, caddr_t arg2); static uint_t pepb_pwr_msi_intr(caddr_t arg, caddr_t arg2); static uint_t pepb_err_msi_intr(caddr_t arg, caddr_t arg2); -static int pepb_is_nvidia_root_port(dev_info_t *); +static int pepb_intr_on_root_port(dev_info_t *); static int pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type); static void pepb_intr_fini(pepb_devstate_t *pepb_p); @@ -378,7 +379,7 @@ pepb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) ddi_driver_name(devi), ddi_get_instance(devi), intr_types)); if (pepb_enable_msi && (intr_types & DDI_INTR_TYPE_MSI) && - pepb_is_nvidia_root_port(devi) == DDI_SUCCESS) { + pepb_intr_on_root_port(devi) == DDI_SUCCESS) { if (pepb_intr_init(pepb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) goto next_step; else @@ -425,9 +426,9 @@ next_step: */ if (pepb->inband_hpc == INBAND_HPC_PCIE && pciehpc_init(devi, NULL) != DDI_SUCCESS) { - pepb->inband_hpc = INBAND_HPC_NONE; - cmn_err(CE_CONT, "!Failed to initialize inband hotplug " - "controller"); + pepb->inband_hpc = INBAND_HPC_NONE; + cmn_err(CE_CONT, "!Failed to initialize inband hotplug " + "controller"); } } @@ -927,7 +928,7 @@ pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type) isr_tab = kmem_alloc(isr_tab_size, KM_SLEEP); if (pepb_enable_msi && pepb_p->intr_count == 2 && intr_type == DDI_INTR_TYPE_MSI && - pepb_is_nvidia_root_port(dip) == DDI_SUCCESS) { + pepb_intr_on_root_port(dip) == DDI_SUCCESS) { isr_tab[0] = pepb_pwr_msi_intr; isr_tab[1] = pepb_err_msi_intr; } else @@ -1054,21 +1055,27 @@ pepb_intx_intr(caddr_t arg, caddr_t arg2) } /* - * pepb_is_nvidia_root_port() + * pepb_intr_on_root_port() * - * This helper function checks if the device is a Nvidia RC or not + * This helper function checks if the device is a Nvidia RC, Intel 5000 or 7300 + * or not */ static int -pepb_is_nvidia_root_port(dev_info_t *dip) +pepb_intr_on_root_port(dev_info_t *dip) { int ret = DDI_FAILURE; ddi_acc_handle_t handle; + uint16_t vendor_id, device_id; if (pci_config_setup(dip, &handle) != DDI_SUCCESS) return (ret); - if ((pci_config_get16(handle, PCI_CONF_VENID) == NVIDIA_VENDOR_ID) && - NVIDIA_PCIE_RC_DEV_ID(pci_config_get16(handle, PCI_CONF_DEVID))) + vendor_id = pci_config_get16(handle, PCI_CONF_VENID); + device_id = pci_config_get16(handle, PCI_CONF_DEVID); + if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(device_id)) + ret = DDI_SUCCESS; + else if ((vendor_id == INTEL_VENDOR_ID) && + INTEL_NB5000_PCIE_DEV_ID(device_id) && !pcie_intel_error_disable) ret = DDI_SUCCESS; pci_config_teardown(&handle); @@ -1107,7 +1114,7 @@ pepb_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle) return (cap_loc == PCI_CAP_NEXT_PTR_NULL ? -1 : pci_config_get16(handle, cap_loc + PCIE_PCIECAP) & - PCIE_PCIECAP_DEV_TYPE_MASK); + PCIE_PCIECAP_DEV_TYPE_MASK); } /*ARGSUSED*/ diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases index 6063299a6d..0fe52eb075 100644 --- a/usr/src/uts/intel/os/driver_aliases +++ b/usr/src/uts/intel/os/driver_aliases @@ -34,3 +34,8 @@ xnbe "xnb,ioemu" xnbo "xnb,SUNW_mac" xnbu "xnb,netfront" pit_beep "SUNW,pit_beep" +intel_nb5000 "pci8086,25d8" +intel_nb5000 "pci8086,25d4" +intel_nb5000 "pci8086,25c0" +intel_nb5000 "pci8086,25d0" +intel_nb5000 "pci8086,3600" diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major index 2e582d62dd..9d983ec052 100644 --- a/usr/src/uts/intel/os/name_to_major +++ b/usr/src/uts/intel/os/name_to_major @@ -116,6 +116,7 @@ pcie_pci 184 kssl 185 mc-amd 186 tzmon 187 +intel_nb5000 188 xpvd 191 xnf 192 xdf 193 diff --git a/usr/src/uts/intel/sys/Makefile b/usr/src/uts/intel/sys/Makefile index a90cb05ca6..3296cb5735 100644 --- a/usr/src/uts/intel/sys/Makefile +++ b/usr/src/uts/intel/sys/Makefile @@ -56,9 +56,9 @@ HDRS = \ machtypes.h \ mc.h \ mc_amd.h \ + mc_intel.h \ mca_amd.h \ mca_x86.h \ - memtest.h \ mii.h \ miipriv.h \ mutex_impl.h \ @@ -95,6 +95,7 @@ HDRS = \ x86_archext.h CLOSEDHDRS = \ + memtest.h \ sbpro.h SUBHDRS = \ diff --git a/usr/src/uts/intel/sys/fm/cpu/AMD.h b/usr/src/uts/intel/sys/fm/cpu/AMD.h index df66719ad3..7908c872a7 100644 --- a/usr/src/uts/intel/sys/fm/cpu/AMD.h +++ b/usr/src/uts/intel/sys/fm/cpu/AMD.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,153 +33,134 @@ extern "C" { #endif +/* + * AMD model-specific ereports. These supplement the generic ereport + * members specified in GMCA.h. + */ + /* 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_BANK_MISC "bank-misc" #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_FLAG_BANK_MISC 0x0000000000000400 - -#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_FLAG_SYND 0x0000000000000001 +#define FM_EREPORT_PAYLOAD_FLAG_SYND_TYPE 0x0000000000000002 +#define FM_EREPORT_PAYLOAD_FLAG_RESOURCE 0x0000000000000004 +#define FM_EREPORT_PAYLOAD_FLAG_STACK 0x0000000000000008 + +/* + * Model specific payload for each ereport type is specified using the + * following groupings of the individual flag values above. + */ +#define FM_EREPORT_PAYLOAD_FLAGS_COMMON 0x0ULL /* empty */ + #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_BANK_MISC \ - (FM_EREPORT_PAYLOAD_FLAG_BANK_MISC) + 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_4(f1, f2, f3, f4) \ - (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAGS_##f1 | \ - FM_EREPORT_PAYLOAD_FLAGS_##f2 | FM_EREPORT_PAYLOAD_FLAGS_##f3 | \ - FM_EREPORT_PAYLOAD_FLAGS_##f4) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) + FM_EREPORT_PAYLOAD_FLAGS_2(SYND, RESOURCE) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_SYS_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) + FM_EREPORT_PAYLOAD_FLAGS_2(SYND, RESOURCE) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_INF_L2_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECC1_UC \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_DATA_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_TAG_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_STAG_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_L1TLB_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_DC_L2TLB_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_SYS_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_L2_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_SYS_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_INF_L2_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_DATA_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_TAG_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_STAG_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_L1TLB_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_IC_L2TLB_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #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) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2D_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_PAR \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_L2T_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, SYND) + FM_EREPORT_PAYLOAD_FLAGS_1(SYND) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_RDE \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECC1 \ - FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) + FM_EREPORT_PAYLOAD_FLAGS_2(SYND, RESOURCE) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_BU_S_ECCM \ - FM_EREPORT_PAYLOAD_FLAGS_3(ADDR, SYND, RESOURCE) + FM_EREPORT_PAYLOAD_FLAGS_2(SYND, RESOURCE) #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_4(ADDR, SYND, RESOURCE, BANK_MISC) + FM_EREPORT_PAYLOAD_FLAGS_2(SYND, RESOURCE) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_MEM_UE \ - FM_EREPORT_PAYLOAD_FLAGS_4(ADDR, SYND, RESOURCE, BANK_MISC) + FM_EREPORT_PAYLOAD_FLAGS_2(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) + FM_EREPORT_PAYLOAD_FLAGS_1(NB) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_TA \ - FM_EREPORT_PAYLOAD_FLAGS_2(ADDR, NB) + FM_EREPORT_PAYLOAD_FLAGS_1(NB) #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_GART_WALK \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_RMW \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_WDOG \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_NB_DRAMADDR_PAR \ FM_EREPORT_PAYLOAD_FLAGS_COMMON #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_MC_TESTFAIL \ FM_EREPORT_PAYLOAD_FLAG_RESOURCE #define FM_EREPORT_PAYLOAD_FLAGS_CPU_AMD_UNKNOWN \ - FM_EREPORT_PAYLOAD_FLAGS_1(ADDR) + FM_EREPORT_PAYLOAD_FLAGS_COMMON #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" diff --git a/usr/src/uts/intel/sys/fm/cpu/GENAMD.h b/usr/src/uts/intel/sys/fm/cpu/GENAMD.h new file mode 100644 index 0000000000..7654877976 --- /dev/null +++ b/usr/src/uts/intel/sys/fm/cpu/GENAMD.h @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FM_CPU_GENAMD_H +#define _SYS_FM_CPU_GENAMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Ereport class subcategory - same as in GMCA.h */ +#define FM_EREPORT_CPU_GENAMD "generic-x86" + +/* Ereport leaf classes */ +#define FM_EREPORT_CPU_GENAMD_MEM_CE "mem_ce" +#define FM_EREPORT_CPU_GENAMD_MEM_UE "mem_ue" +#define FM_EREPORT_CPU_GENAMD_CKMEM_CE "mem_ce" +#define FM_EREPORT_CPU_GENAMD_CKMEM_UE "mem_ue" +#define FM_EREPORT_CPU_GENADM_GARTTBLWLK "gart_tbl_walk" + +#define _FM_EREPORT_FLAG(n) (1ULL << (n)) + +#define FM_EREPORT_GENAMD_PAYLOAD_NAME_SYND "syndrome" +#define FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND _FM_EREPORT_FLAG(1) + +#define FM_EREPORT_GENAMD_PAYLOAD_NAME_CKSYND "syndrome" +#define FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND _FM_EREPORT_FLAG(2) + +#define FM_EREPORT_GENAMD_PAYLOAD_NAME_SYNDTYPE "syndrome-type" +#define FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE _FM_EREPORT_FLAG(3) + +#define FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCE "resource" +#define FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE _FM_EREPORT_FLAG(4) + +#define FM_EREPORT_GENAMD_PAYLOAD_NAME_RESOURCECNT "resource_counts" +#define FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCECNT _FM_EREPORT_FLAG(5) + +#define FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_CE \ + (FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCECNT) + +#define FM_EREPORT_GENAMD_PAYLOAD_FLAGS_MEM_UE \ + (FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYND | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCECNT) + +#define FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_CE \ + (FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCECNT) + +#define FM_EREPORT_GENAMD_PAYLOAD_FLAGS_CKMEM_UE \ + (FM_EREPORT_GENAMD_PAYLOAD_FLAG_CKSYND | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_SYNDTYPE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCE | \ + FM_EREPORT_GENAMD_PAYLOAD_FLAG_RESOURCECNT) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FM_CPU_GENAMD_H */ diff --git a/usr/src/uts/intel/sys/fm/cpu/GMCA.h b/usr/src/uts/intel/sys/fm/cpu/GMCA.h new file mode 100644 index 0000000000..c677efad87 --- /dev/null +++ b/usr/src/uts/intel/sys/fm/cpu/GMCA.h @@ -0,0 +1,201 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FM_CPU_GMCA_H +#define _SYS_FM_CPU_GMCA_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Generic x86 cpu ereports. + * + * On a machine-check exception, or on a periodic poll for error status + * of a cpu, we read through all the MCA banks of the processor and + * log an ereport for each MCA bank that contains valid error telemetry. + * These ereports will all share the same detector FMRI and ENA. + * + * Since we have no model-specific knowledge of the cpu we cannot + * decode all details of the error, so we'll stick to the "architectural" + * bits. Similarly since we have no associated memory-controller driver + * or detailed topology information we cannot produce "resource" ereport + * FMRIs. + */ + +/* Ereport class subcategory for generic x86 processors */ +#define FM_EREPORT_CPU_GENERIC "generic-x86" + +/* + * Simple error code ereport leaf classes + */ +#define FM_EREPORT_CPU_GENERIC_UNCLASSIFIED "unclassified" +#define FM_EREPORT_CPU_GENERIC_MC_CODE_PARITY "microcode_rom_parity" +#define FM_EREPORT_CPU_GENERIC_EXTERNAL "external" +#define FM_EREPORT_CPU_GENERIC_FRC "frc" +#define FM_EREPORT_CPU_GENERIC_INTERNAL_TIMER "internal_timer" +#define FM_EREPORT_CPU_GENERIC_INTERNAL_UNCLASS "internal_unclassified" + +/* + * Leaf class to be used when we can match no simple or compound class + */ +#define FM_EREPORT_CPU_GENERIC_UNKNOWN "unknown" + +/* + * Compound error code ereport leaf classes. The arguments for snprintf + * will appear in the following order: + * + * 1 - TT interpretation + * 2 - LL interpretation + * 3 - RRRR interpretation + * 4 - PP interpretation + * 5 - II interpretation + * 6 - T interpretation + * 7 - "_uc" if this is a compound error with MCi_STATUS.UC set, else "" + * + * They can be selected in the format string using the %n$s specifier form. + * + * The set of interpretations that we expand to will not be exactly the + * same as the set of mnemonics described in Vol 3A (see the + * FM_EREPORT_PAYLOAD_NAME_COMPOUND_ERR ereport payload member for that). + * Instead we wish to compress the possible ereport classes that can + * be generated by pushing things such as "LG" for generic cache level + * down to "". + * + * + * "Memory Hierarchy" in compound errors actually refers to CPU cache + * memory. + */ +#define FM_EREPORT_CPU_GENERIC_GENMEMHIER "%2$s" "cache" "%7$s" +#define FM_EREPORT_CPU_GENERIC_TLB "%2$s" "%1$s" "tlb" "%7$s" +#define FM_EREPORT_CPU_GENERIC_MEMHIER "%2$s" "%1$s" "cache" "%7$s" +#define FM_EREPORT_CPU_GENERIC_BUS_INTERCONNECT "bus_interconnect" "%5$s" "%7$s" + +/* + * The "interpretation" expansions for the above ereport leaf subclasses. + */ +#define FM_EREPORT_CPU_GENERIC_TT_INSTR "i" +#define FM_EREPORT_CPU_GENERIC_TT_DATA "d" +#define FM_EREPORT_CPU_GENERIC_TT_GEN "" + +#define FM_EREPORT_CPU_GENERIC_LL_L0 "l0" +#define FM_EREPORT_CPU_GENERIC_LL_L1 "l1" +#define FM_EREPORT_CPU_GENERIC_LL_L2 "l2" +#define FM_EREPORT_CPU_GENERIC_LL_LG "" + +#define FM_EREPORT_CPU_GENERIC_RRRR_ERR "" +#define FM_EREPORT_CPU_GENERIC_RRRR_RD "" +#define FM_EREPORT_CPU_GENERIC_RRRR_WR "" +#define FM_EREPORT_CPU_GENERIC_RRRR_DRD "" +#define FM_EREPORT_CPU_GENERIC_RRRR_DWR "" +#define FM_EREPORT_CPU_GENERIC_RRRR_IRD "" +#define FM_EREPORT_CPU_GENERIC_RRRR_PREFETCH "" +#define FM_EREPORT_CPU_GENERIC_RRRR_EVICT "" +#define FM_EREPORT_CPU_GENERIC_RRRR_SNOOP "" + +#define FM_EREPORT_CPU_GENERIC_PP_SRC "" +#define FM_EREPORT_CPU_GENERIC_PP_RES "" +#define FM_EREPORT_CPU_GENERIC_PP_OBS "" +#define FM_EREPORT_CPU_GENERIC_PP_GEN "" + +#define FM_EREPORT_CPU_GENERIC_II_MEM "_memory" +#define FM_EREPORT_CPU_GENERIC_II_IO "_io" +#define FM_EREPORT_CPU_GENERIC_II_GEN "" + +#define FM_EREPORT_CPU_GENERIC_T_NOTIMEOUT "" +#define FM_EREPORT_CPU_GENERIC_T_TIMEOUT "" + +/* + * Ereport payload member names together with bitmask values to select + * their inclusion in ereports. + */ + +#define _FM_EREPORT_FLAG(n) (1ULL << (n)) + +#define FM_EREPORT_PAYLOAD_FLAG_COMPOUND_ERR _FM_EREPORT_FLAG(1) +#define FM_EREPORT_PAYLOAD_NAME_COMPOUND_ERR "compound_errorname" + +#define FM_EREPORT_PAYLOAD_FLAG_MCG_STATUS _FM_EREPORT_FLAG(2) +#define FM_EREPORT_PAYLOAD_NAME_MCG_STATUS "IA32_MCG_STATUS" +#define FM_EREPORT_PAYLOAD_NAME_MCG_STATUS_MCIP "machine_check_in_progress" + +#define FM_EREPORT_PAYLOAD_FLAG_IP _FM_EREPORT_FLAG(3) +#define FM_EREPORT_PAYLOAD_NAME_IP "ip" + +#define FM_EREPORT_PAYLOAD_FLAG_PRIV _FM_EREPORT_FLAG(4) +#define FM_EREPORT_PAYLOAD_NAME_PRIV "privileged" + +#define FM_EREPORT_PAYLOAD_FLAG_BANK_NUM _FM_EREPORT_FLAG(5) +#define FM_EREPORT_PAYLOAD_NAME_BANK_NUM "bank_number" +#define FM_EREPORT_PAYLOAD_NAME_BANK_MSR_OFFSET "bank_msr_offset" + +#define FM_EREPORT_PAYLOAD_FLAG_MC_STATUS _FM_EREPORT_FLAG(6) +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS "IA32_MCi_STATUS" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_OVER "overflow" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_UC "error_uncorrected" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_EN "error_enabled" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_PCC "processor_context_corrupt" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_TES "threshold_based_error_status" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_ERRCODE "error_code" +#define FM_EREPORT_PAYLOAD_NAME_MC_STATUS_EXTERRCODE "model_specific_error_code" + +#define FM_EREPORT_PAYLOAD_FLAG_MC_ADDR _FM_EREPORT_FLAG(7) +#define FM_EREPORT_PAYLOAD_NAME_MC_ADDR "IA32_MCi_ADDR" + +#define FM_EREPORT_PAYLOAD_FLAG_MC_MISC _FM_EREPORT_FLAG(8) +#define FM_EREPORT_PAYLOAD_NAME_MC_MISC "IA32_MCi_MISC" + +#define FM_EREPORT_PAYLOAD_FLAG_DISP _FM_EREPORT_FLAG(9) +#define FM_EREPORT_PAYLOAD_NAME_DISP "disp" + +/* + * Common combinations of payload members + */ +#define FM_EREPORT_PAYLOAD_FLAGS_GLOBAL \ + (FM_EREPORT_PAYLOAD_FLAG_MCG_STATUS | \ + FM_EREPORT_PAYLOAD_FLAG_IP | \ + FM_EREPORT_PAYLOAD_FLAG_PRIV) + +#define FM_EREPORT_PAYLOAD_FLAGS_BANK \ + (FM_EREPORT_PAYLOAD_FLAG_BANK_NUM | \ + FM_EREPORT_PAYLOAD_FLAG_MC_STATUS | \ + FM_EREPORT_PAYLOAD_FLAG_MC_ADDR | \ + FM_EREPORT_PAYLOAD_FLAG_MC_MISC | \ + FM_EREPORT_PAYLOAD_FLAG_DISP) + +#define FM_EREPORT_PAYLOAD_FLAGS_COMMON \ + (FM_EREPORT_PAYLOAD_FLAGS_GLOBAL | FM_EREPORT_PAYLOAD_FLAGS_BANK) + +#define FM_EREPORT_PAYLOAD_FLAGS_COMPOUND_ERR \ + (FM_EREPORT_PAYLOAD_FLAGS_COMMON | FM_EREPORT_PAYLOAD_FLAG_COMPOUND_ERR) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FM_CPU_GMCA_H */ diff --git a/usr/src/uts/intel/sys/mc.h b/usr/src/uts/intel/sys/mc.h index 4e1ab003ee..27ef52684d 100644 --- a/usr/src/uts/intel/sys/mc.h +++ b/usr/src/uts/intel/sys/mc.h @@ -18,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,12 +41,13 @@ extern "C" { #define MC_UNUM_NDIMM 2 typedef struct mc_unum { - int unum_board; - int unum_chip; - int unum_mc; - int unum_cs; - int unum_rank; - uint64_t unum_offset; + int unum_board; /* system board */ + int unum_chip; /* chip/socket */ + int unum_mc; /* memory-controller or branch */ + int unum_chan; /* DRAM channel */ + int unum_cs; /* chip-select */ + int unum_rank; /* rank */ + uint64_t unum_offset; /* row, column, bank-select etc */ int unum_dimms[MC_UNUM_NDIMM]; } mc_unum_t; @@ -55,8 +56,6 @@ typedef struct mc_unum { */ #define MC_INVALNUM ((uint32_t)-1) -#define MC_AMD_DEV_OFFSET 24 /* node ID + offset == PCI dev num */ - /* * /dev/mc/mc* ioctl cmds */ diff --git a/usr/src/uts/intel/sys/mc_amd.h b/usr/src/uts/intel/sys/mc_amd.h index 6e799b2cf2..79b03d9e70 100644 --- a/usr/src/uts/intel/sys/mc_amd.h +++ b/usr/src/uts/intel/sys/mc_amd.h @@ -28,6 +28,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include <sys/mc.h> +#include <sys/isa_defs.h> #include <sys/x86_archext.h> #ifdef __cplusplus @@ -124,13 +125,16 @@ extern "C" { /* * Memory controller registers are read via PCI config space accesses on - * bus 0, device 24 + NodeId, and function as follows: + * bus 0, device 0x18 + NodeId, and function as follows: * * Function 0: HyperTransport Technology Configuration * Function 1: Address Map * Function 2: DRAM Controller & HyperTransport Technology Trace Mode * Function 3: Miscellaneous Control */ + +#define MC_AMD_DEV_OFFSET 0x18 /* node ID + offset == PCI dev num */ + enum mc_funcnum { MC_FUNC_HTCONFIG = 0, MC_FUNC_ADDRMAP = 1, @@ -197,7 +201,17 @@ enum mc_funcnum { #define MC_CTL_REG_SPARECTL 0xb0 /* On-line spare control register */ /* - * Registers will be represented as unions, with one fixed-width unsigned + * MC4_MISC MSR and MC4_MISCj MSRs + */ +#define MC_MSR_NB_MISC0 0x413 +#define MC_MSR_NB_MISC1 0xc0000408 +#define MC_MSR_NB_MISC2 0xc0000409 +#define MC_MSR_NB_MISC3 0xc000040a +#define MC_MSR_NB_MISC(j) \ + ((j) == 0 ? MC_MSR_NB_MISC0 : MC_MSR_NB_MISC1 + (j) - 1) + +/* + * PCI registers will be represented as unions, with one fixed-width unsigned * integer member providing access to the raw register value and one or more * structs breaking the register out into bitfields (more than one struct if * the register definitions varies across processor revisions). @@ -209,8 +223,9 @@ enum mc_funcnum { * processor revision to which it applies. At this point the only xxx * values in use are: * 'cmn' - applies to all revisions - * 'preF' - applies to revisions E and earlier - * 'revFG' - applies to revisions F and G + * 'f_preF' - applies to revisions E and earlier + * 'f_revFG' - applies to revisions F and G + * * Variants such as 'preD', 'revDE', 'postCG' etc should be introduced * as requirements arise. The MC_REV_* and MC_REV_MATCH etc macros * will also need to grow to match. Use MCREG_FIELD_* to access the @@ -222,20 +237,26 @@ enum mc_funcnum { */ #define MC_REV_UNKNOWN X86_CHIPREV_UNKNOWN -#define MC_REV_B X86_CHIPREV_AMD_F_REV_B -#define MC_REV_C (X86_CHIPREV_AMD_F_REV_C0 | X86_CHIPREV_AMD_F_REV_CG) -#define MC_REV_D X86_CHIPREV_AMD_F_REV_D -#define MC_REV_E X86_CHIPREV_AMD_F_REV_E -#define MC_REV_F X86_CHIPREV_AMD_F_REV_F -#define MC_REV_G X86_CHIPREV_AMD_F_REV_G + +#define MC_F_REV_B X86_CHIPREV_AMD_F_REV_B +#define MC_F_REV_C (X86_CHIPREV_AMD_F_REV_C0 | X86_CHIPREV_AMD_F_REV_CG) +#define MC_F_REV_D X86_CHIPREV_AMD_F_REV_D +#define MC_F_REV_E X86_CHIPREV_AMD_F_REV_E +#define MC_F_REV_F X86_CHIPREV_AMD_F_REV_F +#define MC_F_REV_G X86_CHIPREV_AMD_F_REV_G + +#define MC_10_REV_A X86_CHIPREV_AMD_10_REV_A +#define MC_10_REV_B X86_CHIPREV_AMD_10_REV_B /* * The most common groupings for memory controller features. */ -#define MC_REVS_BC (MC_REV_B | MC_REV_C) -#define MC_REVS_DE (MC_REV_D | MC_REV_E) -#define MC_REVS_BCDE (MC_REVS_BC | MC_REVS_DE) -#define MC_REVS_FG (MC_REV_F | MC_REV_G) +#define MC_F_REVS_BC (MC_F_REV_B | MC_F_REV_C) +#define MC_F_REVS_DE (MC_F_REV_D | MC_F_REV_E) +#define MC_F_REVS_BCDE (MC_F_REVS_BC | MC_F_REVS_DE) +#define MC_F_REVS_FG (MC_F_REV_F | MC_F_REV_G) + +#define MC_10_REVS_AB (MC_10_REV_A | MC_10_REV_B) /* * Is 'rev' included in the 'revmask' bitmask? @@ -251,9 +272,30 @@ enum mc_funcnum { #define MCREG_VAL32(up) ((up)->_val32) +/* + * Access a field that has the same structure in all families and revisions + */ #define MCREG_FIELD_CMN(up, field) _MCREG_FIELD(up, cmn, field) -#define MCREG_FIELD_preF(up, field) _MCREG_FIELD(up, preF, field) -#define MCREG_FIELD_revFG(up, field) _MCREG_FIELD(up, revFG, field) + +/* + * Access a field as defined for family 0xf prior to revision F + */ +#define MCREG_FIELD_F_preF(up, field) _MCREG_FIELD(up, f_preF, field) + +/* + * Access a field as defined for family 0xf revisions F and G + */ +#define MCREG_FIELD_F_revFG(up, field) _MCREG_FIELD(up, f_revFG, field) + +/* + * Access a field as defined for family 0x10 revisions A and + */ +#define MCREG_FIELD_10_revAB(up, field) _MCREG_FIELD(up, 10_revAB, field) + +/* + * We will only define the register bitfields for little-endian order + */ +#ifdef _BIT_FIELDS_LTOH /* * Function 0 - HT Configuration: Routing Table Node Register @@ -370,7 +412,7 @@ union mcreg_dramhole { union mcreg_csbase { uint32_t _val32; /* - * Register format in revisions E and earlier + * Register format in family 0xf revisions E and earlier */ struct { uint32_t CSEnable:1; /* 0:0 - CS Bank Enable */ @@ -378,9 +420,9 @@ union mcreg_csbase { uint32_t BaseAddrLo:7; /* 15:9 - Base Addr 19:13 */ uint32_t reserved2:5; /* 20:16 */ uint32_t BaseAddrHi:11; /* 31:21 - Base Addr 35:25 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t CSEnable:1; /* 0:0 - CS Bank Enable */ @@ -391,14 +433,14 @@ union mcreg_csbase { uint32_t reserved2:5; /* 18:14 */ uint32_t BaseAddrHi:10; /* 28:19 - Base Addr 36:27 */ uint32_t reserved3:3; /* 31:39 */ - } _fmt_revFG; + } _fmt_f_revFG; }; -#define MC_CSBASE(up, rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? \ - (uint64_t)MCREG_FIELD_revFG(up, BaseAddrHi) << 27 | \ - (uint64_t)MCREG_FIELD_revFG(up, BaseAddrLo) << 13 : \ - (uint64_t)MCREG_FIELD_preF(up, BaseAddrHi) << 25 | \ - (uint64_t)MCREG_FIELD_preF(up, BaseAddrLo) << 13) +#define MC_CSBASE(up, rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? \ + (uint64_t)MCREG_FIELD_F_revFG(up, BaseAddrHi) << 27 | \ + (uint64_t)MCREG_FIELD_F_revFG(up, BaseAddrLo) << 13 : \ + (uint64_t)MCREG_FIELD_F_preF(up, BaseAddrHi) << 25 | \ + (uint64_t)MCREG_FIELD_F_preF(up, BaseAddrLo) << 13) /* * Function 2 - DRAM Controller: DRAM CS Mask Registers @@ -407,7 +449,7 @@ union mcreg_csbase { union mcreg_csmask { uint32_t _val32; /* - * Register format in revisions E and earlier + * Register format in family 0xf revisions E and earlier */ struct { uint32_t reserved1:9; /* 8:0 */ @@ -415,9 +457,9 @@ union mcreg_csmask { uint32_t reserved2:5; /* 20:16 */ uint32_t AddrMaskHi:9; /* 29:21 - Addr Mask 33:25 */ uint32_t reserved3:2; /* 31:30 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t reserved1:5; /* 4:0 */ @@ -425,22 +467,22 @@ union mcreg_csmask { uint32_t reserved2:5; /* 18:14 */ uint32_t AddrMaskHi:10; /* 28:19 - Addr Mask 36:27 */ uint32_t reserved3:3; /* 31:29 */ - } _fmt_revFG; + } _fmt_f_revFG; }; -#define MC_CSMASKLO_LOBIT(rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? 13 : 13) -#define MC_CSMASKLO_HIBIT(rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? 21 : 19) +#define MC_CSMASKLO_LOBIT(rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? 13 : 13) +#define MC_CSMASKLO_HIBIT(rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? 21 : 19) -#define MC_CSMASKHI_LOBIT(rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? 27 : 25) -#define MC_CSMASKHI_HIBIT(rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? 36 : 33) +#define MC_CSMASKHI_LOBIT(rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? 27 : 25) +#define MC_CSMASKHI_HIBIT(rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? 36 : 33) -#define MC_CSMASK_UNMASKABLE(rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? 0 : 2) +#define MC_CSMASK_UNMASKABLE(rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? 0 : 2) -#define MC_CSMASK(up, rev) (MC_REV_MATCH(rev, MC_REVS_FG) ? \ - (uint64_t)MCREG_FIELD_revFG(up, AddrMaskHi) << 27 | \ - (uint64_t)MCREG_FIELD_revFG(up, AddrMaskLo) << 13 | 0x7c01fff : \ - (uint64_t)MCREG_FIELD_preF(up, AddrMaskHi) << 25 | \ - (uint64_t)MCREG_FIELD_preF(up, AddrMaskLo) << 13 | 0x1f01fff) +#define MC_CSMASK(up, rev) (MC_REV_MATCH(rev, MC_F_REVS_FG) ? \ + (uint64_t)MCREG_FIELD_F_revFG(up, AddrMaskHi) << 27 | \ + (uint64_t)MCREG_FIELD_F_revFG(up, AddrMaskLo) << 13 | 0x7c01fff : \ + (uint64_t)MCREG_FIELD_F_preF(up, AddrMaskHi) << 25 | \ + (uint64_t)MCREG_FIELD_F_preF(up, AddrMaskLo) << 13 | 0x1f01fff) /* * Function 2 - DRAM Controller: DRAM Bank Address Mapping Registers @@ -449,7 +491,7 @@ union mcreg_csmask { union mcreg_bankaddrmap { uint32_t _val32; /* - * Register format in revisions E and earlier + * Register format in family 0xf revisions E and earlier */ struct { uint32_t cs10:4; /* 3:0 - CS1/0 */ @@ -459,9 +501,9 @@ union mcreg_bankaddrmap { uint32_t reserved1:14; /* 29:16 */ uint32_t BankSwizzleMode:1; /* 30:30 */ uint32_t reserved2:1; /* 31:31 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t cs10:4; /* 3:0 - CS1/0 */ @@ -469,7 +511,7 @@ union mcreg_bankaddrmap { uint32_t cs54:4; /* 11:8 - CS5/4 */ uint32_t cs76:4; /* 15:12 - CS7/6 */ uint32_t reserved1:16; /* 31:16 */ - } _fmt_revFG; + } _fmt_f_revFG; /* * Accessing all mode encodings as one uint16 */ @@ -492,7 +534,7 @@ union mcreg_bankaddrmap { union mcreg_dramcfg_lo { uint32_t _val32; /* - * Register format in revisions E and earlier. + * Register format in family 0xf revisions E and earlier. * Bit 7 is a BIOS ScratchBit in revs D and earlier, * PwrDwnTriEn in revision E; we don't use it so * we'll call it ambig1. @@ -523,9 +565,9 @@ union mcreg_dramcfg_lo { uint32_t En2T:1; /* 28 */ uint32_t UpperCSMap:1; /* 29 */ uint32_t PwrDownCtl:2; /* 31:30 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t InitDram:1; /* 0 */ @@ -543,7 +585,7 @@ union mcreg_dramcfg_lo { uint32_t reserved3:2; /* 18:17 */ uint32_t DimmEccEn:1; /* 19 */ uint32_t reserved4:12; /* 31:20 */ - } _fmt_revFG; + } _fmt_f_revFG; }; /* @@ -553,7 +595,7 @@ union mcreg_dramcfg_lo { union mcreg_drammisc { uint32_t _val32; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t reserved2:1; /* 0 */ @@ -566,13 +608,13 @@ union mcreg_drammisc { uint32_t PwrSavingsEn:1; /* 10 */ uint32_t reserved1:13; /* 23:11 */ uint32_t MemClkDis:8; /* 31:24 */ - } _fmt_revFG; + } _fmt_f_revFG; }; union mcreg_dramcfg_hi { uint32_t _val32; /* - * Register format in revisions E and earlier. + * Register format in family 0xf revisions E and earlier. */ struct { uint32_t AsyncLat:4; /* 3:0 */ @@ -592,9 +634,9 @@ union mcreg_dramcfg_hi { uint32_t MC3_EN:1; /* 29 */ uint32_t reserved4:1; /* 30 */ uint32_t OddDivisorCorrect:1; /* 31 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t MemClkFreq:3; /* 2:0 */ @@ -615,7 +657,7 @@ union mcreg_dramcfg_hi { uint32_t undocumented1:1; /* 23 */ uint32_t DcqBypassMax:4; /* 27:24 */ uint32_t FourActWindow:4; /* 31:28 */ - } _fmt_revFG; + } _fmt_f_revFG; }; /* @@ -634,6 +676,23 @@ union mcreg_scrubctl { } _fmt_cmn; }; +union mcreg_dramscrublo { + uint32_t _val32; + struct { + uint32_t ScrubReDirEn:1; /* 0 */ + uint32_t reserved:5; /* 5:1 */ + uint32_t ScrubAddrLo:26; /* 31:6 */ + } _fmt_cmn; +}; + +union mcreg_dramscrubhi { + uint32_t _val32; + struct { + uint32_t ScrubAddrHi:8; /* 7:0 */ + uint32_t reserved:24; /* 31:8 */ + } _fmt_cmn; +}; + /* * Function 3 - Miscellaneous Control: On-Line Spare Control Register */ @@ -641,7 +700,7 @@ union mcreg_scrubctl { union mcreg_nbcfg { uint32_t _val32; /* - * Register format in revisions E and earlier. + * Register format in family 0xf revisions E and earlier. */ struct { uint32_t CpuEccErrEn:1; /* 0 */ @@ -668,9 +727,9 @@ union mcreg_nbcfg { uint32_t reserved2:1; /* 26 */ uint32_t NbMcaToMstCpuEn:1; /* 27 */ uint32_t reserved3:4; /* 31:28 */ - } _fmt_preF; + } _fmt_f_preF; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t CpuEccErrEn:1; /* 0 */ @@ -701,7 +760,7 @@ union mcreg_nbcfg { uint32_t SyncOnDramAdrParErrEn:1; /* 30 */ uint32_t reserved3:1; /* 31 */ - } _fmt_revFG; + } _fmt_f_revFG; }; /* @@ -711,7 +770,7 @@ union mcreg_nbcfg { union mcreg_sparectl { uint32_t _val32; /* - * Register format in revisions F and G + * Register format in family 0xf revisions F and G */ struct { uint32_t SwapEn:1; /* 0 */ @@ -728,9 +787,126 @@ union mcreg_sparectl { uint32_t EccErrCntWrEn:1; /* 23 */ uint32_t EccErrCnt:4; /* 27:24 */ uint32_t reserved5:4; /* 31:28 */ - } _fmt_revFG; + } _fmt_f_revFG; + /* + * Regiser format in family 0x10 revisions A and B + */ + struct { + uint32_t SwapEn0:1; /* 0 */ + uint32_t SwapDone0:1; /* 1 */ + uint32_t SwapEn1:1; /* 2 */ + uint32_t SwapDone1:1; /* 3 */ + uint32_t BadDramCs0:3; /* 6:4 */ + uint32_t reserved1:1; /* 7 */ + uint32_t BadDramCs1:3; /* 10:8 */ + uint32_t reserved2:1; /* 11 */ + uint32_t SwapDoneInt:2; /* 13:12 */ + uint32_t EccErrInt:2; /* 15:14 */ + uint32_t EccErrCntDramCs:4; /* 19:16 */ + uint32_t EccErrCntDramChan:2; /* 21:20 */ + uint32_t reserved4:1; /* 22 */ + uint32_t EccErrCntWrEn:1; /* 23 */ + uint32_t EccErrCnt:4; /* 27:24 */ + uint32_t LvtOffset:4; /* 31:28 */ + } _fmt_10_revAB; }; +/* + * Since the NB is on-chip some registers are also accessible as MSRs. + * We will represent such registers as bitfields as in the 32-bit PCI + * registers above, with the restriction that we must compile for 32-bit + * kernels and so 64-bit bitfields cannot be used. + */ + +#define _MCMSR_FIELD(up, revsuffix, field) ((up)->_fmt_##revsuffix.field) + +#define MCMSR_VAL(up) ((up)->_val64) + +#define MCMSR_FIELD_CMN(up, field) _MCMSR_FIELD(up, cmn, field) +#define MCMSR_FIELD_F_preF(up, field) _MCMSR_FIELD(up, f_preF, field) +#define MCMSR_FIELD_F_revFG(up, field) _MCMSR_FIELD(up, f_revFG, field) +#define MCMSR_FIELD_10_revAB(up, field) _MCMSR_FIELD(up, 10_revAB, field) + +/* + * The NB MISC registers. On family 0xf rev F this was introduced with + * a 12-bit ECC error count of all ECC errors observed on this memory- + * controller (regardless of channel or chip-select) and the ability to + * raise an interrupt or SMI on overflow. In family 0x10 it has a similar + * purpose, but the register is is split into 4 misc registers + * MC4_MISC{0,1,2,3} accessible via both MSRs and PCI config space; + * they perform thresholding for dram, l3, HT errors. + */ + +union mcmsr_nbmisc { + uint64_t _val64; + /* + * MSR format in family 0xf revision F and later + */ + struct { + /* + * Lower 32 bits + */ + struct { + uint32_t _reserved; /* 31:0 */ + } _mcimisc_lo; + /* + * Upper 32 bits + */ + struct { + uint32_t _ErrCount:12; /* 43:32 */ + uint32_t _reserved1:4; /* 47:44 */ + uint32_t _Ovrflw:1; /* 48 */ + uint32_t _IntType:2; /* 50:49 */ + uint32_t _CntEn:1; /* 51 */ + uint32_t _LvtOff:4; /* 55:52 */ + uint32_t _reserved2:5; /* 60:56 */ + uint32_t _Locked:1; /* 61 */ + uint32_t _CntP:1; /* 62 */ + uint32_t _Valid:1; /* 63 */ + } _mcimisc_hi; + } _fmt_f_revFG; + /* + * MSR format in family 0x10 revisions A and B + */ + struct { + /* + * Lower 32 bits + */ + struct { + uint32_t _reserved:24; /* 23:0 */ + uint32_t _BlkPtr:8; /* 31:24 */ + } _mcimisc_lo; + /* + * Upper 32 bits + */ + struct { + uint32_t _ErrCnt:12; /* 43:32 */ + uint32_t _reserved1:4; /* 47:44 */ + uint32_t _Ovrflw:1; /* 48 */ + uint32_t _IntType:2; /* 50:49 */ + uint32_t _CntEn:1; /* 51 */ + uint32_t _LvtOff:4; /* 55:52 */ + uint32_t _reserved2:5; /* 60:56 */ + uint32_t _Locked:1; /* 61 */ + uint32_t _CntP:1; /* 62 */ + uint32_t _Valid:1; /* 63 */ + + } _mcimisc_hi; + } _fmt_10_revAB; +}; + +#define mcmisc_BlkPtr _mcimisc_lo._BlkPtr +#define mcmisc_ErrCount _mcimisc_hi._ErrCount +#define mcmisc_Ovrflw _mcimisc_hi._Ovrflw +#define mcmisc_IntType _mcimisc_hi._IntType +#define mcmisc_CntEn _mcimisc_hi._CntEn +#define mcmisc_LvtOff _mcimisc_hi._LvtOff +#define mcmisc_Locked _mcimisc_hi._Locked +#define mcmisc_CntP _mcimisc_hi._CntP +#define mcmisc_Valid _mcimisc_hi._Valid + +#endif /* _BIT_FIELDS_LTOH */ + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/intel/sys/mc_intel.h b/usr/src/uts/intel/sys/mc_intel.h new file mode 100644 index 0000000000..1ade228526 --- /dev/null +++ b/usr/src/uts/intel/sys/mc_intel.h @@ -0,0 +1,186 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MC_INTEL_H +#define _MC_INTEL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FM_EREPORT_CPU_INTEL "intel" + +#define MCINTEL_NVLIST_VERSTR "mcintel-nvlist-version" +#define MCINTEL_NVLIST_VERS0 0 + +#define MCINTEL_NVLIST_VERS MCINTEL_NVLIST_VERS0 + +#define MCINTEL_NVLIST_MC "memory-channels" +#define MCINTEL_NVLIST_DIMMS "memory-dimms" +#define MCINTEL_NVLIST_DIMMSZ "memory-dimm-size" +#define MCINTEL_NVLIST_RANKS "dimm-ranks" +#define MCINTEL_NVLIST_ROWS "dimm-rows" +#define MCINTEL_NVLIST_COL "dimm-column" +#define MCINTEL_NVLIST_BANK "dimm-banks" +#define MCINTEL_NVLIST_WIDTH "dimm-width" +#define MCINTEL_NVLIST_MID "dimm-manufacture-id" +#define MCINTEL_NVLIST_MLOC "dimm-manufacture-location" +#define MCINTEL_NVLIST_MWEEK "dimm-manufacture-week" +#define MCINTEL_NVLIST_MYEAR "dimm-manufacture-year" +#define MCINTEL_NVLIST_SERIALNO "dimm-serial-number" +#define MCINTEL_NVLIST_PARTNO "dimm-part-number" +#define MCINTEL_NVLIST_REV "dimm-part-rev" + +#define FM_EREPORT_PAYLOAD_NAME_FERR_GLOBAL "ferr_global" +#define FM_EREPORT_PAYLOAD_NAME_NERR_GLOBAL "nerr_global" +#define FM_EREPORT_PAYLOAD_NAME_FSB "fsb" +#define FM_EREPORT_PAYLOAD_NAME_FERR_FAT_FSB "ferr_fat_fsb" +#define FM_EREPORT_PAYLOAD_NAME_NERR_FAT_FSB "nerr_fat_fsb" +#define FM_EREPORT_PAYLOAD_NAME_FERR_NF_FSB "ferr_nf_fsb" +#define FM_EREPORT_PAYLOAD_NAME_NERR_NF_FSB "nerr_nf_fsb" +#define FM_EREPORT_PAYLOAD_NAME_NRECFSB "nrecfsb" +#define FM_EREPORT_PAYLOAD_NAME_NRECFSB_ADDR "nrecfsb_addr" +#define FM_EREPORT_PAYLOAD_NAME_RECFSB "recfsb" +#define FM_EREPORT_PAYLOAD_NAME_PEX "pex" +#define FM_EREPORT_PAYLOAD_NAME_PEX_FAT_FERR "pex_fat_ferr" +#define FM_EREPORT_PAYLOAD_NAME_PEX_FAT_NERR "pex_fat_nerr" +#define FM_EREPORT_PAYLOAD_NAME_PEX_NF_CORR_FERR "pex_nf_corr_ferr" +#define FM_EREPORT_PAYLOAD_NAME_PEX_NF_CORR_NERR "pex_nf_corr_nerr" +#define FM_EREPORT_PAYLOAD_NAME_UNCERRSEV "uncerrsev" +#define FM_EREPORT_PAYLOAD_NAME_RPERRSTS "rperrsts" +#define FM_EREPORT_PAYLOAD_NAME_RPERRSID "rperrsid" +#define FM_EREPORT_PAYLOAD_NAME_UNCERRSTS "uncerrsts" +#define FM_EREPORT_PAYLOAD_NAME_AERRCAPCTRL "aerrcapctrl" +#define FM_EREPORT_PAYLOAD_NAME_CORERRSTS "corerrsts" +#define FM_EREPORT_PAYLOAD_NAME_PEXDEVSTS "pexdevsts" +#define FM_EREPORT_PAYLOAD_NAME_FERR_FAT_INT "ferr_fat_int" +#define FM_EREPORT_PAYLOAD_NAME_FERR_NF_INT "ferr_nf_int" +#define FM_EREPORT_PAYLOAD_NAME_NERR_FAT_INT "nerr_fat_int" +#define FM_EREPORT_PAYLOAD_NAME_NERR_NF_INT "nerr_nf_int" +#define FM_EREPORT_PAYLOAD_NAME_NRECINT "nrecint" +#define FM_EREPORT_PAYLOAD_NAME_RECINT "recint" +#define FM_EREPORT_PAYLOAD_NAME_NRECSF "nrecsf" +#define FM_EREPORT_PAYLOAD_NAME_RECSF "recsf" +#define FM_EREPORT_PAYLOAD_NAME_RANK "rank" +#define FM_EREPORT_PAYLOAD_NAME_BANK "bank" +#define FM_EREPORT_PAYLOAD_NAME_CAS "cas" +#define FM_EREPORT_PAYLOAD_NAME_RAS "ras" +#define FM_EREPORT_PAYLOAD_NAME_FERR_FAT_FBD "ferr_fat_fbd" +#define FM_EREPORT_PAYLOAD_NAME_NERR_FAT_FBD "nerr_fat_fbd" +#define FM_EREPORT_PAYLOAD_NAME_NRECMEMA "nrecmema" +#define FM_EREPORT_PAYLOAD_NAME_NRECMEMB "nrecmemb" +#define FM_EREPORT_PAYLOAD_NAME_NRECFGLOG "nrecfglog" +#define FM_EREPORT_PAYLOAD_NAME_NRECFBDA "nrecfbda" +#define FM_EREPORT_PAYLOAD_NAME_NRECFBDB "nrecfbdb" +#define FM_EREPORT_PAYLOAD_NAME_NRECFBDC "nrecfbdc" +#define FM_EREPORT_PAYLOAD_NAME_NRECFBDD "nrecfbdd" +#define FM_EREPORT_PAYLOAD_NAME_NRECFBDE "nrecfbde" +#define FM_EREPORT_PAYLOAD_NAME_SPCPC "spcpc" +#define FM_EREPORT_PAYLOAD_NAME_SPCPS "spcps" +#define FM_EREPORT_PAYLOAD_NAME_UERRCNT "uerrcnt" +#define FM_EREPORT_PAYLOAD_NAME_UERRCNT_LAST "uerrcnt_last" +#define FM_EREPORT_PAYLOAD_NAME_BADRAMA "badrama" +#define FM_EREPORT_PAYLOAD_NAME_BADRAMB "badramb" +#define FM_EREPORT_PAYLOAD_NAME_BADCNT "badcnt" +#define FM_EREPORT_PAYLOAD_NAME_MC "mc" +#define FM_EREPORT_PAYLOAD_NAME_MCA "mca" +#define FM_EREPORT_PAYLOAD_NAME_TOLM "tolm" +#define FM_EREPORT_PAYLOAD_NAME_MIR "mir" +#define FM_EREPORT_PAYLOAD_NAME_MTR "mtr" +#define FM_EREPORT_PAYLOAD_NAME_DMIR "dmir" +#define FM_EREPORT_PAYLOAD_NAME_FERR_NF_FBD "ferr_nf_fbd" +#define FM_EREPORT_PAYLOAD_NAME_NERR_NF_FBD "nerr_nf_fbd" +#define FM_EREPORT_PAYLOAD_NAME_RECMEMA "recmema" +#define FM_EREPORT_PAYLOAD_NAME_RECMEMB "recmemb" +#define FM_EREPORT_PAYLOAD_NAME_RECFGLOG "recfglog" +#define FM_EREPORT_PAYLOAD_NAME_RECFBDA "recfbda" +#define FM_EREPORT_PAYLOAD_NAME_RECFBDB "recfbdb" +#define FM_EREPORT_PAYLOAD_NAME_RECFBDC "recfbdc" +#define FM_EREPORT_PAYLOAD_NAME_RECFBDD "recfbdd" +#define FM_EREPORT_PAYLOAD_NAME_RECFBDE "recfbde" +#define FM_EREPORT_PAYLOAD_NAME_CERRCNT "cerrcnt" +#define FM_EREPORT_PAYLOAD_NAME_CERRCNT_LAST "cerrcnt_last" +#define FM_EREPORT_PAYLOAD_NAME_PCISTS "pcists" +#define FM_EREPORT_PAYLOAD_NAME_PEXDEVSTS "pexdevsts" +#define FM_EREPORT_PAYLOAD_NAME_ERROR_NO "intel-error-list" + +#define FM_EREPORT_PAYLOAD_NAME_ADDR "addr" +#define FM_EREPORT_PAYLOAD_NAME_BANK_NUM "bank-number" +#define FM_EREPORT_PAYLOAD_NAME_BANK_MISC "bank-misc" +#define FM_EREPORT_PAYLOAD_NAME_BANK_STAT "bank-status" +#define FM_EREPORT_PAYLOAD_NAME_BANK_OFFSET "bank-offset" +#define FM_EREPORT_PAYLOAD_NAME_MC_TYPE "mc-type" +#define FM_EREPORT_PAYLOAD_CPUID "cpuid" + +#define FM_EREPORT_PAYLOAD_BQR "Bus-queue-request" +#define FM_EREPORT_PAYLOAD_BQET "Bus-queue-error-type" +#define FM_EREPORT_PAYLOAD_FRC "FRC-error" +#define FM_EREPORT_PAYLOAD_BERR "BERR" +#define FM_EREPORT_PAYLOAD_INT_BINT "Internal-BINT" +#define FM_EREPORT_PAYLOAD_EXT_BINT "External-BINT" +#define FM_EREPORT_PAYLOAD_BUS_BINT "Bus-BINT" +#define FM_EREPORT_PAYLOAD_TO_BINT "Timeout-BINT" +#define FM_EREPORT_PAYLOAD_HARD "Hard-error" +#define FM_EREPORT_PAYLOAD_IERR "IERR" +#define FM_EREPORT_PAYLOAD_AERR "AERR" +#define FM_EREPORT_PAYLOAD_UERR "UERR" +#define FM_EREPORT_PAYLOAD_CECC "CECC" +#define FM_EREPORT_PAYLOAD_UECC "UECC" +#define FM_EREPORT_PAYLOAD_ECC_SYND "ECC-syndrome" + +#define FM_EREPORT_PAYLOAD_FSB_PARITY "fsb-address-parity" +#define FM_EREPORT_PAYLOAD_RESP_HF "response-hard-fail" +#define FM_EREPORT_PAYLOAD_RESP_PARITY "response-parity" +#define FM_EREPORT_PAYLOAD_DATA_PARITY "bus-data-parity" +#define FM_EREPORT_PAYLOAD_INV_PIC "invalid-pic-request" +#define FM_EREPORT_PAYLOAD_PAD_SM "pad-state-machine" +#define FM_EREPORT_PAYLOAD_PAD_SG "pad-strobe-glitch" + +#define FM_EREPORT_PAYLOAD_TAG "tag-error" +#define FM_EREPORT_PAYLOAD_TAG_CLEAN "clean" +#define FM_EREPORT_PAYLOAD_TAG_HIT "hit" +#define FM_EREPORT_PAYLOAD_TAG_MISS "miss" +#define FM_EREPORT_PAYLOAD_DATA "data-error" +#define FM_EREPORT_PAYLOAD_DATA_SINGLE "single-bit" +#define FM_EREPORT_PAYLOAD_DATA_DBL_CLEAN "double-bit-clean" +#define FM_EREPORT_PAYLOAD_DATA_DBL_MOD "double-bit-modified" +#define FM_EREPORT_PAYLOAD_L3 "l3-cache" +#define FM_EREPORT_PAYLOAD_INV_PIC "invalid-pic-request" +#define FM_EREPORT_PAYLOAD_CACHE_NERRORS "cache-error-count" + +#define INTEL_NB_5000P 0x25d88086 +#define INTEL_NB_5000V 0x25d48086 +#define INTEL_NB_5000X 0x25c08086 +#define INTEL_NB_5000Z 0x25d08086 +#define INTEL_NB_7300 0x36008086 + +#ifdef __cplusplus +} +#endif + +#endif /* _MC_INTEL_H */ diff --git a/usr/src/uts/intel/sys/mca_amd.h b/usr/src/uts/intel/sys/mca_amd.h index 5adbf02965..d583b71221 100644 --- a/usr/src/uts/intel/sys/mca_amd.h +++ b/usr/src/uts/intel/sys/mca_amd.h @@ -29,8 +29,10 @@ #pragma ident "%Z%%M% %I% %E% SMI" +#include <sys/mca_x86.h> + /* - * Constants the Memory Check Architecture as implemented on AMD CPUs. + * Constants for the Machine Check Architecture as implemented on AMD CPUs. */ #ifdef __cplusplus @@ -83,9 +85,6 @@ extern "C" { #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 @@ -298,14 +297,18 @@ extern "C" { #define AMD_NB_CFG_GENCRCERRBYTE0 0x00010000 #define AMD_NB_CFG_GENCRCERRBYTE1 0x00020000 -/* Generic bank status register bits */ -#define AMD_BANK_STAT_VALID 0x8000000000000000ULL -#define AMD_BANK_STAT_OVER 0x4000000000000000ULL -#define AMD_BANK_STAT_UC 0x2000000000000000ULL -#define AMD_BANK_STAT_EN 0x1000000000000000ULL -#define AMD_BANK_STAT_MISCV 0x0800000000000000ULL -#define AMD_BANK_STAT_ADDRV 0x0400000000000000ULL -#define AMD_BANK_STAT_PCC 0x0200000000000000ULL +/* + * The AMD extended error code is just one nibble of the upper 16 bits + * of the bank status (the resy being used for syndrome etc). So we use + * AMD_EXT_ERRCODE to retrieve that extended error code, not the generic + * MCAX86_MSERRCODE. + */ +#define _AMD_ERREXT_MASK 0x00000000000f0000ULL +#define _AMD_ERREXT_SHIFT 16 +#define AMD_EXT_ERRCODE(stat) \ + (((stat) & _AMD_ERREXT_MASK) >> _AMD_ERREXT_SHIFT) +#define AMD_EXT_MKERRCODE(errcode) \ + (((errcode) << _AMD_ERREXT_SHIFT) & _AMD_ERREXT_MASK) #define AMD_BANK_STAT_CECC 0x0000400000000000ULL #define AMD_BANK_STAT_UECC 0x0000200000000000ULL @@ -338,87 +341,26 @@ extern "C" { ((((uint64_t)(synd) << AMD_NB_STAT_CKSYND_SHIFT) & \ AMD_NB_STAT_CKSYND_MASK) | AMD_BANK_MKSYND(synd)) -#define AMD_ERRCODE_MASK 0x000000000000ffffULL #define AMD_ERREXT_MASK 0x00000000000f0000ULL #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_MKTLB(tt, ll) MCAX86_MKERRCODE_TLB(tt, ll) +#define AMD_ERRCODE_ISTLB(code) MCAX86_ERRCODE_ISTLB(code) + +#define AMD_ERRCODE_MKMEM(r4, tt, ll) MCAX86_MKERRCODE_MEMHIER(r4, tt, ll) +#define AMD_ERRCODE_ISMEM(code) MCAX86_ERRCODE_ISMEMHIER(code) #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) + MCAX86_MKERRCODE_BUS_INTERCONNECT(pp, t, r4, ii, ll) +#define AMD_ERRCODE_ISBUS(code) MCAX86_ERRCODE_ISBUS_INTERCONNECT(code) #define AMD_NB_ADDRLO_MASK 0xfffffff8 #define AMD_NB_ADDRHI_MASK 0x000000ff @@ -437,11 +379,12 @@ extern "C" { #define AMD_NB_SCRUBCTL_RATE_MAX 0x16 #define AMD_NB_SCRUBADDR_LO_MASK 0xffffffc0 +#define AMD_NB_SCRUBADDR_LO_SHIFT 6 #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) + (((addr) & AMD_NB_SCRUBADDR_LO_MASK) >> AMD_NB_SCRUBADDR_LO_SHIFT) #define AMD_NB_SCRUBADDR_MKHI(addr) \ (((addr) >> 32) & AMD_NB_SCRUBADDR_HI_MASK) diff --git a/usr/src/uts/intel/sys/mca_x86.h b/usr/src/uts/intel/sys/mca_x86.h index 78066bdc63..91b9a5bbfc 100644 --- a/usr/src/uts/intel/sys/mca_x86.h +++ b/usr/src/uts/intel/sys/mca_x86.h @@ -2,9 +2,8 @@ * 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. + * Common Development and Distribution License (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. @@ -19,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,91 +32,350 @@ * CPUs. */ +#include <sys/types.h> +#include <sys/isa_defs.h> + #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. + * Architectural MSRs from the IA-32 Software Developer's Manual - IA32_MSR_* */ #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_TES_P 0x00000800ULL + +#define MCG_CAP_COUNT_MASK 0x000000ffULL +#define MCG_CAP_COUNT(cap) ((cap) & MCG_CAP_COUNT_MASK) + #define MCG_CAP_EXT_CNT_MASK 0x00ff0000ULL #define MCG_CAP_EXT_CNT_SHIFT 16 +#define MCG_CAP_EXT_CNT(cap) \ + (((cap) & MCG_CAP_EXT_CNT_MASK) >> MCG_CAP_EXT_CNT_SHIFT) #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 +/* + * There are as many error detector "banks" as indicated by + * IA32_MSR_MCG_CAP.COUNT. Each bank has a minimum of 3 associated + * registers (MCi_CTL, MCi_STATUS, and MCi_ADDR) and some banks + * may implement a fourth (MCi_MISC) which should only be read + * when MCi_STATUS.MISCV indicates that it exists and has valid data. + * + * The first bank features at MSR offsets 0x400 to 0x403, the next at + * 0x404 to 0x407, and so on. Current processors implement up to 6 + * banks (sixth one at 0x414 to 0x417). + * + * It is, sadly, not the case that the i'th set of 4 registers starting + * at 0x400 corresponds to MCi_{CTL,STATUS,ADDR,MISC} - for some Intel + * processors, for example, the order is 0/1/2/4/3. Nonetheless, we can + * still iterate through the banks and read all telemetry - there'll just + * be some potential confusion as to which processor unit a bank is + * associated with. Error reports should seek to disambiguate. + * + * IA32_MSR_MC(i, which) calculates the MSR address for th i'th bank + * of registers (not for MCi_*, as above) and one of CTL, STATUS, ADDR, MISC + */ + +#define _IA32_MSR_MC0_CTL 0x400ULL /* first/base reg */ +#define _IA32_MSR_OFFSET_CTL 0x0 /* offset within a bank */ +#define _IA32_MSR_OFFSET_STATUS 0x1 /* offset within a bank */ +#define _IA32_MSR_OFFSET_ADDR 0x2 /* offset within a bank */ +#define _IA32_MSR_OFFSET_MISC 0x3 /* offset within a bank */ + + +#define IA32_MSR_MC(i, which) \ + (_IA32_MSR_MC0_CTL + (i) * 4 + _IA32_MSR_OFFSET_##which) + +/* + * IA32_MSR_MCG_CAP.MCG_EXT_P indicates that a processor implements + * a set of extended machine-check registers starting at MSR 0x180; + * when that is set, IA32_MSR_MCG_CAP.MCG_EXT_CNT indicates how + * many of these extended registers (addresses 0x180, 0x181, ...) + * are present. Which registers are present depends on whether support + * for 64-bit architecture is present. + */ -#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_MCG_RAX 0x180ULL /* first/base extended reg */ -#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_EXT(i) (_IA32_MCG_RAX + (i)) -#define IA32_MSR_MC3_CTL 0x40c -#define IA32_MSR_MC3_STATUS 0x40d -#define IA32_MSR_MC3_ADDR 0x40e -#define IA32_MSR_MC3_MISC 0x40f +#ifdef _BIT_FIELDS_LTOH +typedef union mca_x86_mcistatus { + uint64_t _val64; + struct { + /* + * Lower 32 bits of MCi_STATUS + */ + struct { + uint32_t _errcode:16; /* <15:0> */ + uint32_t _ms_errcode:16; /* <31:16> */ + } _mcis_lo; + /* + * Upper 32 bits of MCi_STATUS + */ + union { + /* + * If IA32_MCG_CAP.MCG_TES_P is set then <54:53> + * and <56:55> are architectural. + */ + struct { + uint32_t _otherinfo:21; /* <52:32> */ + uint32_t _tbes:2; /* <54:53> */ + uint32_t _reserved:2; /* <56:55> */ + uint32_t _pcc:1; /* <57> */ + uint32_t _addrv:1; /* <58> */ + uint32_t _miscv:1; /* <59> */ + uint32_t _en:1; /* <60> */ + uint32_t _uc:1; /* <61> */ + uint32_t _over:1; /* <62> */ + uint32_t _val:1; /* <63> */ + } _mcis_hi_tes_p; + /* + * If IA32_MCG_CAP.MCG_TES_P is clear then <56:53> + * are model-specific. + */ + struct { + uint32_t _otherinfo:25; /* <56:32> */ + uint32_t _pcc:1; /* <57> */ + uint32_t _addrv:1; /* <58> */ + uint32_t _miscv:1; /* <59> */ + uint32_t _en:1; /* <60> */ + uint32_t _uc:1; /* <61> */ + uint32_t _over:1; /* <62> */ + uint32_t _val:1; /* <63> */ + } _mcis_hi_tes_np; + } _mcis_hi; + } _mcis_hilo; +} mca_x86_mcistatus_t; + +#define mcistatus_errcode _mcis_hilo._mcis_lo._errcode +#define mcistatus_mserrcode _mcis_hilo._mcis_lo._ms_errcode +#define mcistatus_pcc _mcis_hilo._mcis_hi._mcis_hi_tes_np._pcc +#define mcistatus_addrv _mcis_hilo._mcis_hi._mcis_hi_tes_np._addrv +#define mcistatus_miscv _mcis_hilo._mcis_hi._mcis_hi_tes_np._miscv +#define mcistatus_en _mcis_hilo._mcis_hi._mcis_hi_tes_np._en +#define mcistatus_uc _mcis_hilo._mcis_hi._mcis_hi_tes_np._uc +#define mcistatus_over _mcis_hilo._mcis_hi._mcis_hi_tes_np._over +#define mcistatus_val _mcis_hilo._mcis_hi._mcis_hi_tes_np._val + +/* + * The consumer must check for TES_P before using these. + */ +#define mcistatus_tbes _mcis_hilo._mcis_hi._mcis_hi_tes_p._tbes +#define mcistatus_reserved \ + _mcis_hilo._mcis_hi._mcis_hi_tes_p._reserved +#define mcistatus_otherinfo_tes_p \ + _mcis_hilo._mcis_hi._mcis_hi_tes_p._otherinfo +#define mcistatus_otherinfo_tes_np \ + _mcis_hilo._mcis_hi._mcis_hi_tes_np._otherinfo + +#endif /* _BIT_FIELDS_LTOH */ #define MSR_MC_STATUS_VAL 0x8000000000000000ULL -#define MSR_MC_STATUS_O 0x4000000000000000ULL +#define MSR_MC_STATUS_OVER 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_RESERVED_MASK 0x0180000000000000ULL +#define MSR_MC_STATUS_TBES_MASK 0x0060000000000000ULL +#define MSR_MC_STATUS_TBES_SHIFT 53 #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. + * Macros to extract error code and model-specific error code. + */ +#define MCAX86_ERRCODE(stat) ((stat) & MSR_MC_STATUS_MCAERR_MASK) +#define MCAX86_MSERRCODE(stat) \ + (((stat) & MSR_MC_STATUS_MSERR_MASK) >> MSR_MC_STATUS_MSERR_SHIFT) + +/* + * Macro to extract threshold based error state (if MCG_CAP.TES_P) */ -#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 +#define MCAX86_TBES_VALUE(stat) \ + (((stat) & MSR_MC_STATUS_TBES_MASK) >> MSR_MC_STATUS_TBES_SHIFT) + +/* + * Bit definitions for the architectural error code. + */ + +#define MCAX86_ERRCODE_TT_MASK 0x000c +#define MCAX86_ERRCODE_TT_SHIFT 2 +#define MCAX86_ERRCODE_TT_INSTR 0x0 +#define MCAX86_ERRCODE_TT_DATA 0x1 +#define MCAX86_ERRCODE_TT_GEN 0x2 +#define MCAX86_ERRCODE_TT(code) \ + (((code) & MCAX86_ERRCODE_TT_MASK) >> MCAX86_ERRCODE_TT_SHIFT) + +#define MCAX86_ERRCODE_LL_MASK 0x0003 +#define MCAX86_ERRCODE_LL_SHIFT 0 +#define MCAX86_ERRCODE_LL_L0 0x0 +#define MCAX86_ERRCODE_LL_L1 0x1 +#define MCAX86_ERRCODE_LL_L2 0x2 +#define MCAX86_ERRCODE_LL_LG 0x3 +#define MCAX86_ERRCODE_LL(code) \ + ((code) & MCAX86_ERRCODE_LL_MASK) + +#define MCAX86_ERRCODE_RRRR_MASK 0x00f0 +#define MCAX86_ERRCODE_RRRR_SHIFT 4 +#define MCAX86_ERRCODE_RRRR_ERR 0x0 +#define MCAX86_ERRCODE_RRRR_RD 0x1 +#define MCAX86_ERRCODE_RRRR_WR 0x2 +#define MCAX86_ERRCODE_RRRR_DRD 0x3 +#define MCAX86_ERRCODE_RRRR_DWR 0x4 +#define MCAX86_ERRCODE_RRRR_IRD 0x5 +#define MCAX86_ERRCODE_RRRR_PREFETCH 0x6 +#define MCAX86_ERRCODE_RRRR_EVICT 0x7 +#define MCAX86_ERRCODE_RRRR_SNOOP 0x8 +#define MCAX86_ERRCODE_RRRR(code) \ + (((code) & MCAX86_ERRCODE_RRRR_MASK) >> MCAX86_ERRCODE_RRRR_SHIFT) + +#define MCAX86_ERRCODE_PP_MASK 0x0600 +#define MCAX86_ERRCODE_PP_SHIFT 9 +#define MCAX86_ERRCODE_PP_SRC 0x0 +#define MCAX86_ERRCODE_PP_RES 0x1 +#define MCAX86_ERRCODE_PP_OBS 0x2 +#define MCAX86_ERRCODE_PP_GEN 0x3 +#define MCAX86_ERRCODE_PP(code) \ + (((code) & MCAX86_ERRCODE_PP_MASK) >> MCAX86_ERRCODE_PP_SHIFT) + +#define MCAX86_ERRCODE_II_MASK 0x000c +#define MCAX86_ERRCODE_II_SHIFT 2 +#define MCAX86_ERRCODE_II_MEM 0x0 +#define MCAX86_ERRCODE_II_IO 0x2 +#define MCAX86_ERRCODE_II_GEN 0x3 +#define MCAX86_ERRCODE_II(code) \ + (((code) & MCAX86_ERRCODE_II_MASK) >> MCAX86_ERRCODE_II_SHIFT) + +#define MCAX86_ERRCODE_T_MASK 0x0100 +#define MCAX86_ERRCODE_T_SHIFT 8 +#define MCAX86_ERRCODE_T_NONE 0x0 +#define MCAX86_ERRCODE_T_TIMEOUT 0x1 +#define MCAX86_ERRCODE_T(code) \ + (((code) & MCAX86_ERRCODE_T_MASK) >> MCAX86_ERRCODE_T_SHIFT) + +/* + * Simple error encoding. MASKON are bits that must be set for a match + * at the same time bits indicated by MASKOFF are clear. + */ +#define MCAX86_SIMPLE_UNCLASSIFIED_MASKON 0x0001 +#define MCAX86_SIMPLE_UNCLASSIFIED_MASKOFF 0xfffe + +#define MCAX86_SIMPLE_MC_CODE_PARITY_MASKON 0x0002 +#define MCAX86_SIMPLE_MC_CODE_PARITY_MASKOFF 0xfffd + +#define MCAX86_SIMPLE_EXTERNAL_MASKON 0x0003 +#define MCAX86_SIMPLE_EXTERNAL_MASKOFF 0xfffc + +#define MCAX86_SIMPLE_FRC_MASKON 0x0004 +#define MCAX86_SIMPLE_FRC_MASKOFF 0xfffb + +#define MCAX86_SIMPLE_INTERNAL_TIMER_MASKON 0x0400 +#define MCAX86_SIMPLE_INTERNAL_TIMER_MASKOFF 0xfbff + +#define MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKON 0x0400 +#define MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKOFF 0xf800 +#define MCAX86_SIMPLE_INTERNAL_UNCLASS_VALUE_MASK 0x03ff + +/* + * Macros to make an internal unclassified error code, and to test if + * a given code is internal unclassified. + */ +#define MCAX86_MKERRCODE_INTERNAL_UNCLASS(val) \ + (MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKON | \ + ((val) & MCAX86_SIMPLE_INTERNAL_UNCLASS_VALUE_MASK)) +#define MCAX86_ERRCODE_ISSIMPLE_INTERNAL_UNCLASS(code) \ + (((code) & MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKON) == \ + MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKON && \ + ((code) & MCAX86_SIMPLE_INTERNAL_UNCLASS_MASK_MASKOFF) == 0 && \ + ((code) & MCAX86_SIMPLE_INTERNAL_UNCLASS_VALUE_MASK) != 0) + +/* + * Is the given error code a simple error encoding? + */ +#define MCAX86_ERRCODE_ISSIMPLE(code) \ + ((code) >= MCAX86_SIMPLE_UNCLASSIFIED_MASKON && \ + (code) <= MCAX86_SIMPLE_FRC_MASKON || \ + (code) == MCAX86_SIMPLE_INTERNAL_TIMER_MASKON || \ + MCAX86_ERRCODE_ISSIMPLE_INTERNAL_UNCLASS(code)) + +/* + * Compound error encoding. We always ignore the 'F' bit (which indicates + * "correction report filtering") in classifying the error type. + */ +#define MCAX86_COMPOUND_GENERIC_MEMHIER_MASKON 0x000c +#define MCAX86_COMPOUND_GENERIC_MEMHIER_MASKOFF 0xeff0 + +#define MCAX86_COMPOUND_TLB_MASKON 0x0010 +#define MCAX86_COMPOUND_TLB_MASKOFF 0xefe0 + +#define MCAX86_COMPOUND_MEMHIER_MASKON 0x0100 +#define MCAX86_COMPOUND_MEMHIER_MASKOFF 0xee00 + +#define MCAX86_COMPOUND_BUS_INTERCONNECT_MASKON 0x0800 +#define MCAX86_COMPOUND_BUS_INTERCONNECT_MASKOFF 0xe000 + +/* + * Macros to make compound error codes and to test for each type. + */ +#define MCAX86_MKERRCODE_GENERIC_MEMHIER(ll) \ + (MCAX86_COMPOUND_GENERIC_MEMHIER_MASKON | \ + ((ll) & MCAX86_ERRCODE_LL_MASK)) +#define MCAX86_ERRCODE_ISGENERIC_MEMHIER(code) \ + (((code) & MCAX86_COMPOUND_GENERIC_MEMHIER_MASKON) == \ + MCAX86_COMPOUND_GENERIC_MEMHIER_MASKON && \ + ((code) & MCAX86_COMPOUND_GENERIC_MEMHIER_MASKOFF) == 0) + +#define MCAX86_MKERRCODE_TLB(tt, ll) \ + (MCAX86_COMPOUND_TLB_MASKON | \ + ((tt) << MCAX86_ERRCODE_TT_SHIFT & MCAX86_ERRCODE_TT_MASK) | \ + ((ll) & MCAX86_ERRCODE_LL_MASK)) +#define MCAX86_ERRCODE_ISTLB(code) \ + (((code) & MCAX86_COMPOUND_TLB_MASKON) == \ + MCAX86_COMPOUND_TLB_MASKON && \ + ((code) & MCAX86_COMPOUND_TLB_MASKOFF) == 0) + +#define MCAX86_MKERRCODE_MEMHIER(rrrr, tt, ll) \ + (MCAX86_COMPOUND_MEMHIER_MASKON | \ + ((rrrr) << MCAX86_ERRCODE_RRRR_SHIFT & MCAX86_ERRCODE_RRRR_MASK) | \ + ((tt) << MCAX86_ERRCODE_TT_SHIFT & MCAX86_ERRCODE_TT_MASK) | \ + ((ll) & MCAX86_ERRCODE_LL_MASK)) +#define MCAX86_ERRCODE_ISMEMHIER(code) \ + (((code) & MCAX86_COMPOUND_MEMHIER_MASKON) == \ + MCAX86_COMPOUND_MEMHIER_MASKON && \ + ((code) & MCAX86_COMPOUND_MEMHIER_MASKOFF) == 0) + +#define MCAX86_MKERRCODE_BUS_INTERCONNECT(pp, t, rrrr, ii, ll) \ + (MCAX86_COMPOUND_BUS_INTERCONNECT_MASKON | \ + ((pp) << MCAX86_ERRCODE_PP_SHIFT & MCAX86_ERRCODE_PP_MASK) | \ + ((t) << MCAX86_ERRCODE_T_SHIFT & MCAX86_ERRCODE_T_MASK) | \ + ((rrrr) << MCAX86_ERRCODE_RRRR_SHIFT & MCAX86_ERRCODE_RRRR_MASK) | \ + ((ii) << MCAX86_ERRCODE_II_SHIFT & MCAX86_ERRCODE_II_MASK) | \ + ((ll) & MCAX86_ERRCODE_LL_MASK)) +#define MCAX86_ERRCODE_ISBUS_INTERCONNECT(code) \ + (((code) & MCAX86_COMPOUND_BUS_INTERCONNECT_MASKON) == \ + MCAX86_COMPOUND_BUS_INTERCONNECT_MASKON && \ + ((code) & MCAX86_COMPOUND_BUS_INTERCONNECT_MASKOFF) == 0) + +#define MCAX86_ERRCODE_ISCOMPOUND(code) \ + (MCAX86_ERRCODE_ISGENERIC_MEMHIER(code) || \ + MCAX86_ERRCODE_ISTLB(code) || \ + MCAX86_ERRCODE_ISMEMHIER(code) \ + MCAX86_ERRCODE_ISBUS_INTERCONNECT(code)) + +#define MCAX86_ERRCODE_UNKNOWN(code) \ + (!MCAX86_ERRCODE_ISSIMPLE(code) && !MCAX86_ERRCODE_ISCOMPOUND(code)) #ifdef __cplusplus } diff --git a/usr/src/uts/intel/sys/memtest.h b/usr/src/uts/intel/sys/memtest.h deleted file mode 100644 index 8e9d4fc8ef..0000000000 --- a/usr/src/uts/intel/sys/memtest.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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/pcb.h b/usr/src/uts/intel/sys/pcb.h index 63325e4cc2..ebc3b4e222 100644 --- a/usr/src/uts/intel/sys/pcb.h +++ b/usr/src/uts/intel/sys/pcb.h @@ -69,6 +69,7 @@ typedef struct pcb { #define CPC_OVERFLOW 0x40 /* performance counters overflowed */ #define REQUEST_STEP 0x100 /* request pending to single-step this lwp */ #define REQUEST_NOSTEP 0x200 /* request pending to disable single-step */ +#define ASYNC_HWERR 0x400 /* hardware error has corrupted context */ /* fpu_flags */ #define FPU_EN 0x1 /* flag signifying fpu in use */ diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h index 90cec07b3a..ff785bca1f 100644 --- a/usr/src/uts/intel/sys/x86_archext.h +++ b/usr/src/uts/intel/sys/x86_archext.h @@ -412,8 +412,8 @@ extern "C" { #define X86_CHIPREV_UNKNOWN 0x0 /* - * Definitions for AMD Family 0xf and AMD Family 0x10. Minor revisions C0 and - * CG are sufficiently different that we will distinguish them; in all other + * Definitions for AMD Family 0xf. Minor revisions C0 and CG are + * sufficiently different that we will distinguish them; in all other * case we will identify the major revision. */ #define X86_CHIPREV_AMD_F_REV_B _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0001) @@ -423,8 +423,14 @@ extern "C" { #define X86_CHIPREV_AMD_F_REV_E _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0010) #define X86_CHIPREV_AMD_F_REV_F _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0020) #define X86_CHIPREV_AMD_F_REV_G _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0xf, 0x0040) -#define X86_CHIPREV_AMD_10_REV_B \ + +/* + * Definitions for AMD Family 0x10. Rev A was Engineering Samples only. + */ +#define X86_CHIPREV_AMD_10_REV_A \ _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x10, 0x0001) +#define X86_CHIPREV_AMD_10_REV_B \ + _X86_CHIPREV_MKREV(X86_VENDOR_AMD, 0x10, 0x0002) /* * Various socket/package types, extended as the need to distinguish |