xref: /OK3568_Linux_fs/external/xserver/os/busfault.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright © 2013 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #ifdef HAVE_DIX_CONFIG_H
24 #include <dix-config.h>
25 #endif
26 
27 #include <X11/Xos.h>
28 #include <X11/Xdefs.h>
29 #include "misc.h"
30 #include <busfault.h>
31 #include <list.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <sys/mman.h>
36 #include <signal.h>
37 
38 struct busfault {
39     struct xorg_list    list;
40 
41     void                *addr;
42     size_t              size;
43 
44     Bool                valid;
45 
46     busfault_notify_ptr notify;
47     void                *context;
48 };
49 
50 static Bool             busfaulted;
51 static struct xorg_list busfaults;
52 
53 struct busfault *
busfault_register_mmap(void * addr,size_t size,busfault_notify_ptr notify,void * context)54 busfault_register_mmap(void *addr, size_t size, busfault_notify_ptr notify, void *context)
55 {
56     struct busfault     *busfault;
57 
58     busfault = calloc(1, sizeof (struct busfault));
59     if (!busfault)
60         return NULL;
61 
62     busfault->addr = addr;
63     busfault->size = size;
64     busfault->notify = notify;
65     busfault->context = context;
66     busfault->valid = TRUE;
67 
68     xorg_list_add(&busfault->list, &busfaults);
69     return busfault;
70 }
71 
72 void
busfault_unregister(struct busfault * busfault)73 busfault_unregister(struct busfault *busfault)
74 {
75     xorg_list_del(&busfault->list);
76     free(busfault);
77 }
78 
79 void
busfault_check(void)80 busfault_check(void)
81 {
82     struct busfault     *busfault, *tmp;
83 
84     if (!busfaulted)
85         return;
86 
87     busfaulted = FALSE;
88 
89     xorg_list_for_each_entry_safe(busfault, tmp, &busfaults, list) {
90         if (!busfault->valid)
91             (*busfault->notify)(busfault->context);
92     }
93 }
94 
95 static void (*previous_busfault_sigaction)(int sig, siginfo_t *info, void *param);
96 
97 static void
busfault_sigaction(int sig,siginfo_t * info,void * param)98 busfault_sigaction(int sig, siginfo_t *info, void *param)
99 {
100     void                *fault = info->si_addr;
101     struct busfault     *iter, *busfault = NULL;
102     void                *new_addr;
103 
104     /* Locate the faulting address in our list of shared segments
105      */
106     xorg_list_for_each_entry(iter, &busfaults, list) {
107 	if ((char *) iter->addr <= (char *) fault && (char *) fault < (char *) iter->addr + iter->size) {
108 	    busfault = iter;
109 	    break;
110 	}
111     }
112     if (!busfault)
113         goto panic;
114 
115     if (!busfault->valid)
116         goto panic;
117 
118     busfault->valid = FALSE;
119     busfaulted = TRUE;
120 
121     /* The client truncated the file; unmap the shared file, map
122      * /dev/zero over that area and keep going
123      */
124 
125     new_addr = mmap(busfault->addr, busfault->size, PROT_READ|PROT_WRITE,
126                     MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0);
127 
128     if (new_addr == MAP_FAILED)
129         goto panic;
130 
131     return;
132 panic:
133     if (previous_busfault_sigaction)
134         (*previous_busfault_sigaction)(sig, info, param);
135     else
136         FatalError("bus error\n");
137 }
138 
139 Bool
busfault_init(void)140 busfault_init(void)
141 {
142     struct sigaction    act, old_act;
143 
144     act.sa_sigaction = busfault_sigaction;
145     act.sa_flags = SA_SIGINFO;
146     sigemptyset(&act.sa_mask);
147     if (sigaction(SIGBUS, &act, &old_act) < 0)
148         return FALSE;
149     previous_busfault_sigaction = old_act.sa_sigaction;
150     xorg_list_init(&busfaults);
151     return TRUE;
152 }
153