1 | /* $Id: nmi.c,v 1.4 2013/11/26 20:29:40 rmind Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c)2009,2011 YAMAMOTO Takashi, |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: nmi.c,v 1.4 2013/11/26 20:29:40 rmind Exp $" ); |
31 | |
32 | /* |
33 | * nmi dispatcher. |
34 | * |
35 | * XXX no need to be nmi-specific. |
36 | * actual assumptions are: |
37 | * - dispatch() is called with preemption disabled. |
38 | * - handlers never block. |
39 | * - establish() and disestablish() are called within a thread context. |
40 | * (thus can block) |
41 | */ |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/kernel.h> |
45 | #include <sys/systm.h> |
46 | #include <sys/atomic.h> |
47 | #include <sys/kmem.h> |
48 | #include <sys/mutex.h> |
49 | #include <sys/pserialize.h> |
50 | |
51 | #include <x86/nmi.h> |
52 | |
53 | struct nmi_handler { |
54 | int (*n_func)(const struct trapframe *, void *); |
55 | void *n_arg; |
56 | struct nmi_handler *n_next; |
57 | }; |
58 | |
59 | static kmutex_t nmi_list_lock; /* serialize establish and disestablish */ |
60 | static pserialize_t nmi_psz; |
61 | static nmi_handler_t *nmi_handlers; /* list of handlers */ |
62 | |
63 | /* |
64 | * nmi_establish: establish an nmi handler |
65 | * |
66 | * => can block. |
67 | * => returns an opaque handle. |
68 | */ |
69 | |
70 | nmi_handler_t * |
71 | nmi_establish(int (*func)(const struct trapframe *, void *), void *arg) |
72 | { |
73 | struct nmi_handler *n; |
74 | |
75 | n = kmem_alloc(sizeof(*n), KM_SLEEP); |
76 | if (n == NULL) { |
77 | return NULL; |
78 | } |
79 | n->n_func = func; |
80 | n->n_arg = arg; |
81 | |
82 | /* |
83 | * put it into the list. |
84 | */ |
85 | |
86 | mutex_enter(&nmi_list_lock); |
87 | n->n_next = nmi_handlers; |
88 | membar_producer(); /* n->n_next should be visible before nmi_handlers */ |
89 | nmi_handlers = n; /* atomic store */ |
90 | mutex_exit(&nmi_list_lock); |
91 | |
92 | return n; |
93 | } |
94 | |
95 | /* |
96 | * nmi_disestablish: disestablish an nmi handler. |
97 | * |
98 | * => can block. |
99 | * => take an opaque handle. it must be one returned by nmi_establish. |
100 | */ |
101 | |
102 | void |
103 | nmi_disestablish(nmi_handler_t *handle) |
104 | { |
105 | nmi_handler_t *n; |
106 | nmi_handler_t **pp; |
107 | |
108 | KASSERT(handle != NULL); |
109 | |
110 | /* |
111 | * remove the handler from the list. |
112 | */ |
113 | |
114 | mutex_enter(&nmi_list_lock); |
115 | for (pp = &nmi_handlers, n = *pp; n != NULL; n = *pp) { |
116 | if (n == handle) { |
117 | break; |
118 | } |
119 | pp = &n->n_next; |
120 | } |
121 | #if defined(DIAGNOSTIC) |
122 | if (n == NULL) { |
123 | mutex_exit(&nmi_list_lock); |
124 | panic("%s: invalid handle %p" , __func__, handle); |
125 | } |
126 | #endif /* defined(DIAGNOSTIC) */ |
127 | *pp = n->n_next; /* atomic store */ |
128 | mutex_exit(&nmi_list_lock); /* mutex_exit implies a store fence */ |
129 | |
130 | /* |
131 | * before freeing 'n', ensure that no other cpus are |
132 | * in the middle of nmi_dispatch. |
133 | */ |
134 | |
135 | pserialize_perform(nmi_psz); |
136 | kmem_free(n, sizeof(*n)); |
137 | } |
138 | |
139 | /* |
140 | * nmi_dispatch: dispatch an nmi. |
141 | * |
142 | * => called by interrupts, thus preempt disabled. |
143 | */ |
144 | |
145 | int |
146 | nmi_dispatch(const struct trapframe *tf) |
147 | { |
148 | const struct nmi_handler *n; |
149 | int handled = 0; |
150 | |
151 | /* |
152 | * XXX abstraction violation |
153 | * |
154 | * we don't bother to call pserialize_read_enter/pserialize_read_exit |
155 | * because they are not necessary here as we are sure our IPL is |
156 | * higher than IPL_SOFTSERIAL. better to avoid unnecessary calls as |
157 | * we are in a dangerous context. (NMI) |
158 | */ |
159 | |
160 | for (n = nmi_handlers; /* atomic load */ |
161 | n != NULL; |
162 | membar_consumer(), n = n->n_next) { /* atomic load */ |
163 | handled |= (*n->n_func)(tf, n->n_arg); |
164 | } |
165 | return handled; |
166 | } |
167 | |
168 | void |
169 | nmi_init(void) |
170 | { |
171 | |
172 | mutex_init(&nmi_list_lock, MUTEX_DEFAULT, IPL_NONE); |
173 | nmi_psz = pserialize_create(); |
174 | } |
175 | |