]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/ia64/ia64/mca.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / ia64 / ia64 / mca.c
1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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.
14  *
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.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/lock.h>
33 #include <sys/malloc.h>
34 #include <sys/mutex.h>
35 #include <sys/sysctl.h>
36 #include <sys/uuid.h>
37 #include <vm/vm.h>
38 #include <vm/vm_kern.h>
39 #include <machine/mca.h>
40 #include <machine/sal.h>
41 #include <machine/smp.h>
42
43 MALLOC_DEFINE(M_MCA, "MCA", "Machine Check Architecture");
44
45 struct mca_info {
46         STAILQ_ENTRY(mca_info) mi_link;
47         char    mi_name[32];
48         size_t  mi_recsz;
49         char    mi_record[0];
50 };
51
52 static STAILQ_HEAD(, mca_info) mca_records =
53     STAILQ_HEAD_INITIALIZER(mca_records);
54
55 int64_t         mca_info_size[SAL_INFO_TYPES];
56 vm_offset_t     mca_info_block;
57 struct mtx      mca_info_block_lock;
58
59 SYSCTL_NODE(_hw, OID_AUTO, mca, CTLFLAG_RW, 0, "MCA container");
60
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. */
64
65 SYSCTL_INT(_hw_mca, OID_AUTO, count, CTLFLAG_RD, &mca_count, 0,
66     "Record count");
67 SYSCTL_INT(_hw_mca, OID_AUTO, first, CTLFLAG_RD, &mca_first, 0,
68     "First record id");
69 SYSCTL_INT(_hw_mca, OID_AUTO, last, CTLFLAG_RD, &mca_last, 0,
70     "Last record id");
71
72 static int
73 mca_sysctl_handler(SYSCTL_HANDLER_ARGS)
74 {
75         int error = 0;
76
77         if (!arg1)
78                 return (EINVAL);
79         error = SYSCTL_OUT(req, arg1, arg2);
80
81         if (error || !req->newptr)
82                 return (error);
83
84         error = SYSCTL_IN(req, arg1, arg2);
85         return (error);
86 }
87
88 void
89 ia64_mca_populate(void)
90 {
91         struct mca_info *rec;
92
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",
101                     "Error record");
102                 mtx_lock_spin(&mca_info_block_lock);
103         }
104         mtx_unlock_spin(&mca_info_block_lock);
105 }
106
107 void
108 ia64_mca_save_state(int type)
109 {
110         struct ia64_sal_result result;
111         struct mca_record_header *hdr;
112         struct mca_info *rec;
113         uint64_t seqnr;
114         size_t recsz;
115
116         /*
117          * Don't try to get the state if we couldn't get the size of
118          * the state information previously.
119          */
120         if (mca_info_size[type] == -1)
121                 return;
122
123         if (mca_info_block == 0)
124                 return;
125
126         mtx_lock_spin(&mca_info_block_lock);
127         while (1) {
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);
132                         return;
133                 }
134
135                 hdr = (struct mca_record_header *)mca_info_block;
136                 recsz = hdr->rh_length;
137                 seqnr = hdr->rh_seqnr;
138
139                 mtx_unlock_spin(&mca_info_block_lock);
140
141                 rec = malloc(sizeof(struct mca_info) + recsz, M_MCA,
142                     M_NOWAIT | M_ZERO);
143                 if (rec == NULL)
144                         /* XXX: Not sure what to do. */
145                         return;
146
147                 sprintf(rec->mi_name, "%lld", (long long)seqnr);
148
149                 mtx_lock_spin(&mca_info_block_lock);
150
151                 /*
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
158                  * and start over.
159                  */
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);
165                                 free(rec, M_MCA);
166                                 mtx_lock_spin(&mca_info_block_lock);
167                                 continue;
168                         }
169                 }
170
171                 rec->mi_recsz = recsz;
172                 bcopy((char*)mca_info_block, rec->mi_record, recsz);
173
174                 if (mca_count > 0) {
175                         if (seqnr < mca_first)
176                                 mca_first = seqnr;
177                         else if (seqnr > mca_last)
178                                 mca_last = seqnr;
179                 } else
180                         mca_first = mca_last = seqnr;
181
182                 mca_count++;
183                 STAILQ_INSERT_TAIL(&mca_records, rec, mi_link);
184
185                 /*
186                  * Clear the state so that we get any other records when
187                  * they exist.
188                  */
189                 result = ia64_sal_entry(SAL_CLEAR_STATE_INFO, type, 0, 0, 0,
190                     0, 0, 0);
191         }
192 }
193
194 void
195 ia64_mca_init(void)
196 {
197         struct ia64_sal_result result;
198         uint64_t max_size;
199         char *p;
200         int i;
201
202         /*
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.
208          */
209         max_size = 0;
210         for (i = 0; i < SAL_INFO_TYPES; i++) {
211                 result = ia64_sal_entry(SAL_GET_STATE_INFO_SIZE, i, 0, 0, 0,
212                     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];
217                 } else
218                         mca_info_size[i] = -1;
219         }
220         max_size = round_page(max_size);
221
222         p = (max_size) ? contigmalloc(max_size, M_TEMP, 0, 0ul,
223             256*1024*1024 - 1, PAGE_SIZE, 256*1024*1024) : NULL;
224         if (p != NULL) {
225                 mca_info_block = IA64_PHYS_TO_RR7(ia64_tpa((u_int64_t)p));
226
227                 if (bootverbose)
228                         printf("MCA: allocated %ld bytes for state info.\n",
229                             max_size);
230         }
231
232         /*
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...
239          */
240         mtx_init(&mca_info_block_lock, "MCA spin lock", NULL, MTX_SPIN);
241
242         /*
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.
246          */
247         for (i = 0; i < SAL_INFO_TYPES; i++)
248                 ia64_mca_save_state(i);
249 }