2 * Copyright (c) 2002 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/sal.h>
41 #include <machine/smp.h>
43 MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture");
46 STAILQ_ENTRY(mca_info) mi_link;
52 static STAILQ_HEAD(, mca_info) mca_records =
53 STAILQ_HEAD_INITIALIZER(mca_records);
55 int64_t mca_info_size[SAL_INFO_TYPES];
56 vm_offset_t mca_info_block;
57 struct mtx mca_info_block_lock;
59 SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RW, 0, "MCA container");
61 static int mca_count; /* Number of records stored. */
62 static int mca_first; /* First (lowest) record ID. */
63 static int mca_last; /* Last (highest) record ID. */
65 SYSCTL_INT(_hw_mca, OID_AUTO, count, CTLFLAG_RD, &mca_count, 0,
67 SYSCTL_INT(_hw_mca, OID_AUTO, first, CTLFLAG_RD, &mca_first, 0,
69 SYSCTL_INT(_hw_mca, OID_AUTO, last, CTLFLAG_RD, &mca_last, 0,
73 mca_sysctl_handler(SYSCTL_HANDLER_ARGS)
79 error = SYSCTL_OUT(req, arg1, arg2);
81 if (error || !req->newptr)
84 error = SYSCTL_IN(req, arg1, arg2);
89 ia64_mca_populate(void)
93 mtx_lock_spin(&mca_info_block_lock);
94 while (!STAILQ_EMPTY(&mca_records)) {
95 rec = STAILQ_FIRST(&mca_records);
96 STAILQ_REMOVE_HEAD(&mca_records, mi_link);
97 mtx_unlock_spin(&mca_info_block_lock);
98 (void)SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_mca),
99 OID_AUTO, rec->mi_name, CTLTYPE_OPAQUE | CTLFLAG_RD,
100 rec->mi_record, rec->mi_recsz, mca_sysctl_handler, "S,MCA",
102 mtx_lock_spin(&mca_info_block_lock);
104 mtx_unlock_spin(&mca_info_block_lock);
108 ia64_mca_save_state(int type)
110 struct ia64_sal_result result;
111 struct mca_record_header *hdr;
112 struct mca_info *rec;
117 * Don't try to get the state if we couldn't get the size of
118 * the state information previously.
120 if (mca_info_size[type] == -1)
123 if (mca_info_block == 0)
126 mtx_lock_spin(&mca_info_block_lock);
128 result = ia64_sal_entry(SAL_GET_STATE_INFO, type, 0,
129 mca_info_block, 0, 0, 0, 0);
130 if (result.sal_status < 0) {
131 mtx_unlock_spin(&mca_info_block_lock);
135 hdr = (struct mca_record_header *)mca_info_block;
136 recsz = hdr->rh_length;
137 seqnr = hdr->rh_seqnr;
139 mtx_unlock_spin(&mca_info_block_lock);
141 rec = malloc(sizeof(struct mca_info) + recsz, M_MCA,
144 /* XXX: Not sure what to do. */
147 sprintf(rec->mi_name, "%lld", (long long)seqnr);
149 mtx_lock_spin(&mca_info_block_lock);
152 * If the info block doesn't have our record anymore because
153 * we temporarily unlocked it, get it again from SAL. I assume
154 * that it's possible that we could get a different record.
155 * I expect this to happen in a SMP configuration where the
156 * record has been cleared by a different processor. So, if
157 * we get a different record we simply abort with this record
160 if (seqnr != hdr->rh_seqnr) {
161 result = ia64_sal_entry(SAL_GET_STATE_INFO, type, 0,
162 mca_info_block, 0, 0, 0, 0);
163 if (seqnr != hdr->rh_seqnr) {
164 mtx_unlock_spin(&mca_info_block_lock);
166 mtx_lock_spin(&mca_info_block_lock);
171 rec->mi_recsz = recsz;
172 bcopy((char*)mca_info_block, rec->mi_record, recsz);
175 if (seqnr < mca_first)
177 else if (seqnr > mca_last)
180 mca_first = mca_last = seqnr;
183 STAILQ_INSERT_TAIL(&mca_records, rec, mi_link);
186 * Clear the state so that we get any other records when
189 result = ia64_sal_entry(SAL_CLEAR_STATE_INFO, type, 0, 0, 0,
197 struct ia64_sal_result result;
203 * Get the sizes of the state information we can get from SAL and
204 * allocate a common block (forgive me my Fortran :-) for use by
205 * support functions. We create a region 7 address to make it
206 * easy on the OS_MCA or OS_INIT handlers to get the state info
207 * under unreliable conditions.
210 for (i = 0; i < SAL_INFO_TYPES; i++) {
211 result = ia64_sal_entry(SAL_GET_STATE_INFO_SIZE, i, 0, 0, 0,
213 if (result.sal_status == 0) {
214 mca_info_size[i] = result.sal_result[0];
215 if (mca_info_size[i] > max_size)
216 max_size = mca_info_size[i];
218 mca_info_size[i] = -1;
220 max_size = round_page(max_size);
222 p = (max_size) ? contigmalloc(max_size, M_TEMP, 0, 0ul,
223 256*1024*1024 - 1, PAGE_SIZE, 256*1024*1024) : NULL;
225 mca_info_block = IA64_PHYS_TO_RR7(ia64_tpa((u_int64_t)p));
228 printf("MCA: allocated %ld bytes for state info.\n",
233 * Initialize the spin lock used to protect the info block. When APs
234 * get launched, there's a short moment of contention, but in all other
235 * cases it's not a hot spot. I think it's possible to have the MCA
236 * handler be called on multiple processors at the same time, but that
237 * should be rare. On top of that, performance is not an issue when
238 * dealing with machine checks...
240 mtx_init(&mca_info_block_lock, "MCA spin lock", NULL, MTX_SPIN);
243 * Get and save any processor and platfom error records. Note that in
244 * a SMP configuration the processor records are for the BSP only. We
245 * let the APs get and save their own records when we wake them up.
247 for (i = 0; i < SAL_INFO_TYPES; i++)
248 ia64_mca_save_state(i);