2 * Copyright (c) 2002-2010 Marcel Moolenaar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/mutex.h>
35 #include <sys/sysctl.h>
38 #include <vm/vm_kern.h>
39 #include <machine/mca.h>
40 #include <machine/pal.h>
41 #include <machine/sal.h>
42 #include <machine/smp.h>
44 MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture");
47 STAILQ_ENTRY(mca_info) mi_link;
54 STAILQ_HEAD(mca_info_list, mca_info);
56 static int64_t mca_info_size[SAL_INFO_TYPES];
57 static vm_offset_t mca_info_block;
58 static struct mtx mca_info_block_lock;
60 SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RW, NULL, "MCA container");
62 static int mca_count; /* Number of records stored. */
63 static int mca_first; /* First (lowest) record ID. */
64 static int mca_last; /* Last (highest) record ID. */
66 SYSCTL_INT(_hw_mca, OID_AUTO, count, CTLFLAG_RD, &mca_count, 0,
68 SYSCTL_INT(_hw_mca, OID_AUTO, first, CTLFLAG_RD, &mca_first, 0,
70 SYSCTL_INT(_hw_mca, OID_AUTO, last, CTLFLAG_RD, &mca_last, 0,
73 static struct mtx mca_sysctl_lock;
76 mca_sysctl_inject(SYSCTL_HANDLER_ARGS)
78 struct ia64_pal_result res;
83 error = sysctl_wire_old_buffer(req, sizeof(u_int));
85 error = sysctl_handle_int(oidp, &val, 0, req);
87 if (error != 0 || req->newptr == NULL)
90 /* For example: val=137 causes a fatal CPU error. */
91 res = ia64_call_pal_stacked(PAL_MC_ERROR_INJECT, val, 0, 0);
92 printf("%s: %#lx, %#lx, %#lx, %#lx\n", __func__, res.pal_status,
93 res.pal_result[0], res.pal_result[1], res.pal_result[2]);
96 SYSCTL_PROC(_hw_mca, OID_AUTO, inject, CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
97 mca_sysctl_inject, "I", "set to trigger a MCA");
100 mca_sysctl_handler(SYSCTL_HANDLER_ARGS)
106 error = SYSCTL_OUT(req, arg1, arg2);
108 if (error || !req->newptr)
111 error = SYSCTL_IN(req, arg1, arg2);
116 ia64_mca_collect_state(int type, struct mca_info_list *reclst)
118 struct ia64_sal_result result;
119 struct mca_record_header *hdr;
120 struct mca_info *rec;
125 * Don't try to get the state if we couldn't get the size of
126 * the state information previously.
128 if (mca_info_size[type] == -1)
131 if (mca_info_block == 0)
135 mtx_lock_spin(&mca_info_block_lock);
136 result = ia64_sal_entry(SAL_GET_STATE_INFO, type, 0,
137 mca_info_block, 0, 0, 0, 0);
138 if (result.sal_status < 0) {
139 mtx_unlock_spin(&mca_info_block_lock);
143 hdr = (struct mca_record_header *)mca_info_block;
144 recsz = hdr->rh_length;
145 seqnr = hdr->rh_seqnr;
147 mtx_unlock_spin(&mca_info_block_lock);
149 rec = malloc(sizeof(struct mca_info) + recsz, M_MCA,
152 /* XXX: Not sure what to do. */
155 rec->mi_seqnr = seqnr;
156 rec->mi_cpuid = PCPU_GET(cpuid);
158 mtx_lock_spin(&mca_info_block_lock);
161 * If the info block doesn't have our record anymore because
162 * we temporarily unlocked it, get it again from SAL. I assume
163 * that it's possible that we could get a different record.
164 * I expect this to happen in a SMP configuration where the
165 * record has been cleared by a different processor. So, if
166 * we get a different record we simply abort with this record
169 if (seqnr != hdr->rh_seqnr) {
170 result = ia64_sal_entry(SAL_GET_STATE_INFO, type, 0,
171 mca_info_block, 0, 0, 0, 0);
172 if (seqnr != hdr->rh_seqnr) {
173 mtx_unlock_spin(&mca_info_block_lock);
179 rec->mi_recsz = recsz;
180 bcopy((char*)mca_info_block, rec->mi_record, recsz);
183 * Clear the state so that we get any other records when
186 result = ia64_sal_entry(SAL_CLEAR_STATE_INFO, type, 0, 0, 0,
189 mtx_unlock_spin(&mca_info_block_lock);
191 STAILQ_INSERT_TAIL(reclst, rec, mi_link);
196 ia64_mca_save_state(int type)
199 struct mca_info_list reclst = STAILQ_HEAD_INITIALIZER(reclst);
200 struct mca_info *rec;
201 struct sysctl_oid *oid;
203 ia64_mca_collect_state(type, &reclst);
205 STAILQ_FOREACH(rec, &reclst, mi_link) {
206 sprintf(name, "%lu", rec->mi_seqnr);
207 oid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca),
208 OID_AUTO, name, CTLFLAG_RW, NULL, name);
212 mtx_lock(&mca_sysctl_lock);
214 if (rec->mi_seqnr < mca_first)
215 mca_first = rec->mi_seqnr;
216 else if (rec->mi_seqnr > mca_last)
217 mca_last = rec->mi_seqnr;
219 mca_first = mca_last = rec->mi_seqnr;
221 mtx_unlock(&mca_sysctl_lock);
223 sprintf(name, "%u", rec->mi_cpuid);
224 SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(oid), rec->mi_cpuid,
225 name, CTLTYPE_OPAQUE | CTLFLAG_RD, rec->mi_record,
226 rec->mi_recsz, mca_sysctl_handler, "S,MCA", "MCA record");
233 struct ia64_sal_result result;
239 * Get the sizes of the state information we can get from SAL and
240 * allocate a common block (forgive me my Fortran :-) for use by
241 * support functions. We create a region 7 address to make it
242 * easy on the OS_MCA or OS_INIT handlers to get the state info
243 * under unreliable conditions.
246 for (i = 0; i < SAL_INFO_TYPES; i++) {
247 result = ia64_sal_entry(SAL_GET_STATE_INFO_SIZE, i, 0, 0, 0,
249 if (result.sal_status == 0) {
250 mca_info_size[i] = result.sal_result[0];
251 if (mca_info_size[i] > max_size)
252 max_size = mca_info_size[i];
254 mca_info_size[i] = -1;
256 max_size = round_page(max_size);
258 p = (max_size) ? contigmalloc(max_size, M_TEMP, 0, 0ul,
259 256*1024*1024 - 1, PAGE_SIZE, 256*1024*1024) : NULL;
261 mca_info_block = IA64_PHYS_TO_RR7(ia64_tpa((u_int64_t)p));
264 printf("MCA: allocated %ld bytes for state info.\n",
269 * Initialize the spin lock used to protect the info block. When APs
270 * get launched, there's a short moment of contention, but in all other
271 * cases it's not a hot spot. I think it's possible to have the MCA
272 * handler be called on multiple processors at the same time, but that
273 * should be rare. On top of that, performance is not an issue when
274 * dealing with machine checks...
276 mtx_init(&mca_info_block_lock, "MCA info lock", NULL, MTX_SPIN);
279 * Serialize sysctl operations with a sleep lock. Note that this
280 * implies that we update the sysctl tree in a context that allows
283 mtx_init(&mca_sysctl_lock, "MCA sysctl lock", NULL, MTX_DEF);
286 * Get and save any processor and platfom error records. Note that in
287 * a SMP configuration the processor records are for the BSP only. We
288 * let the APs get and save their own records when we wake them up.
290 for (i = 0; i < SAL_INFO_TYPES; i++)
291 ia64_mca_save_state(i);