6e391f27770d1c57fbdeaa2c3f70aa996eccc26b
[sbcl.git] / src / runtime / linux-os.c
1 /*
2  * the Linux incarnation of OS-dependent routines.  See also
3  * $(sbcl_arch)-linux-os.c
4  *
5  * This file (along with os.h) exports an OS-independent interface to
6  * the operating system VM facilities. Surprise surprise, this
7  * interface looks a lot like the Mach interface (but simpler in some
8  * places). For some operating systems, a subset of these functions
9  * will have to be emulated.
10  */
11
12 /*
13  * This software is part of the SBCL system. See the README file for
14  * more information.
15  *
16  * This software is derived from the CMU CL system, which was
17  * written at Carnegie Mellon University and released into the
18  * public domain. The software is in the public domain and is
19  * provided with absolutely no warranty. See the COPYING and CREDITS
20  * files for more information.
21  */
22
23 #include <stdio.h>
24 #include <sys/param.h>
25 #include <sys/file.h>
26 #include "sbcl.h"
27 #include "./signal.h"
28 #include "os.h"
29 #include "arch.h"
30 #include "globals.h"
31 #include "sbcl.h"
32 #include "interrupt.h"
33 #include "interr.h"
34 #include "lispregs.h"
35 #include <sys/socket.h>
36 #include <sys/utsname.h>
37
38 #include <sys/types.h>
39 #include <signal.h>
40 /* #include <sys/sysinfo.h> */
41 #include <sys/time.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <linux/version.h>
45
46 #include "validate.h"
47 #include "thread.h"
48 size_t os_vm_page_size;
49
50 #include "gc.h"
51 \f
52 int linux_sparc_siginfo_bug = 0;
53
54 void os_init(void)
55 {
56     /* Conduct various version checks: do we have enough mmap(), is
57      * this a sparc running 2.2, can we do threads? */
58     {
59         struct utsname name;
60         int major_version;
61         int minor_version;
62         char *p;
63         uname(&name);
64         p=name.release;  
65         major_version = atoi(p);
66         p=strchr(p,'.')+1;
67         minor_version = atoi(p);
68         if (major_version<2) {
69             lose("linux kernel version too old: major version=%d (can't run in version < 2.0.0)",
70                  major_version);
71         }
72 #ifdef LISP_FEATURE_SB_THREAD
73         if ((major_version <2) || (major_version==2 && minor_version < 4)) {
74             lose("linux kernel 2.4 required for thread-enabled SBCL");
75         }
76 #endif
77 #ifdef LISP_FEATURE_SPARC
78         if ((major_version <2) || (major_version==2 && minor_version < 4)) {
79             FSHOW((stderr,"linux kernel %d.%d predates 2.4;\n enabling workarounds for SPARC kernel bugs in signal handling.\n", major_version,minor_version));
80             linux_sparc_siginfo_bug = 1;
81         }
82 #endif
83     }
84
85     os_vm_page_size = getpagesize();
86     /* This could just as well be in arch_init(), but it's not. */
87 #ifdef LISP_FEATURE_X86
88     /* FIXME: This used to be here.  However, I have just removed it
89        with no apparent ill effects (it may be that earlier kernels
90        started up a process with a different set of traps, or
91        something?) Find out what this was meant to do, and reenable it
92        or delete it if possible. -- CSR, 2002-07-15 */
93     /* SET_FPU_CONTROL_WORD(0x1372|4|8|16|32);  no interrupts */
94 #endif
95 }
96
97
98 #ifdef LISP_FEATURE_ALPHA
99 /* The Alpha is a 64 bit CPU.  SBCL is a 32 bit application.  Due to all
100  * the places that assume we can get a pointer into a fixnum with no 
101  * information loss, we have to make sure it allocates all its ram in the
102  * 0-2Gb region.  */
103
104 static void * under_2gb_free_pointer=DYNAMIC_1_SPACE_END;
105 #endif
106
107 os_vm_address_t
108 os_validate(os_vm_address_t addr, os_vm_size_t len)
109 {
110     int flags =  MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
111     os_vm_address_t actual ;
112
113     if (addr) 
114         flags |= MAP_FIXED;
115 #ifdef LISP_FEATURE_ALPHA
116     else {
117         flags |= MAP_FIXED;
118         addr=under_2gb_free_pointer;
119     }
120 #endif  
121     actual = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);
122     if (actual == MAP_FAILED || (addr && (addr!=actual))) {
123         perror("mmap");
124         return 0;               /* caller should check this */
125     }
126
127 #ifdef LISP_FEATURE_ALPHA
128
129     len=(len+(os_vm_page_size-1))&(~(os_vm_page_size-1));
130     under_2gb_free_pointer+=len;
131 #endif
132
133     return actual;
134 }
135
136 void
137 os_invalidate(os_vm_address_t addr, os_vm_size_t len)
138 {
139     if (munmap(addr,len) == -1) {
140         perror("munmap");
141     }
142 }
143
144 os_vm_address_t
145 os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len)
146 {
147     addr = mmap(addr, len,
148                 OS_VM_PROT_ALL,
149                 MAP_PRIVATE | MAP_FILE | MAP_FIXED,
150                 fd, (off_t) offset);
151
152     if (addr == MAP_FAILED) {
153         perror("mmap");
154         lose("unexpected mmap(..) failure");
155     }
156
157     return addr;
158 }
159
160 void
161 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
162 {
163     if (mprotect(address, length, prot) == -1) {
164         perror("mprotect");
165     }
166 }
167 \f
168 /* FIXME: Now that FOO_END, rather than FOO_SIZE, is the fundamental
169  * description of a space, we could probably punt this and just do
170  * (FOO_START <= x && x < FOO_END) everywhere it's called. */
171 static boolean
172 in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
173 {
174     char* beg = (char*)((long)sbeg);
175     char* end = (char*)((long)sbeg) + slen;
176     char* adr = (char*)a;
177     return (adr >= beg && adr < end);
178 }
179
180 boolean
181 is_valid_lisp_addr(os_vm_address_t addr)
182 {
183     struct thread *th;
184     if(in_range_p(addr, READ_ONLY_SPACE_START, READ_ONLY_SPACE_SIZE) ||
185        in_range_p(addr, STATIC_SPACE_START   , STATIC_SPACE_SIZE) ||
186        in_range_p(addr, DYNAMIC_SPACE_START  , DYNAMIC_SPACE_SIZE))
187         return 1;
188     for_each_thread(th) {
189         if((th->control_stack_start <= addr) && (addr < th->control_stack_end))
190             return 1;
191         if(in_range_p(addr, th->binding_stack_start, BINDING_STACK_SIZE))
192             return 1;
193     }
194     return 0;
195 }
196 \f
197 /*
198  * any OS-dependent special low-level handling for signals
199  */
200
201
202 #if defined LISP_FEATURE_GENCGC
203
204 /*
205  * The GENCGC needs to be hooked into whatever signal is raised for
206  * page fault on this OS.
207  */
208 void
209 sigsegv_handler(int signal, siginfo_t *info, void* void_context)
210 {
211     os_context_t *context = arch_os_get_context(&void_context);
212     void* fault_addr = (void*)context->uc_mcontext.cr2;
213     if (!gencgc_handle_wp_violation(fault_addr)) 
214         if(!handle_control_stack_guard_triggered(context,fault_addr))
215             interrupt_handle_now(signal, info, void_context);
216 }
217
218 #else
219
220 static void
221 sigsegv_handler(int signal, siginfo_t *info, void* void_context)
222 {
223     os_context_t *context = arch_os_get_context(&void_context);
224     os_vm_address_t addr;
225
226     addr = arch_get_bad_addr(signal,info,context);
227     if (addr != NULL && 
228         *os_context_register_addr(context,reg_ALLOC) & (1L<<63)){
229         
230         /* Alpha stuff: This is the end of a pseudo-atomic section
231          * during which a signal was received.  We must deal with the
232          * pending interrupt (see also interrupt.c,
233          * ../code/interrupt.lisp)
234          */
235         /* (how we got here: when interrupting, we set bit 63 in
236          * reg_Alloc.  At the end of the atomic section we tried to
237          * write to reg_ALLOC, got a SIGSEGV (there's nothing mapped
238          * there) so ended up here
239          */
240         *os_context_register_addr(context,reg_ALLOC) -= (1L<<63);
241         interrupt_handle_pending(context);
242     } else {
243         if(!interrupt_maybe_gc(signal, info, context))
244             if(!handle_control_stack_guard_triggered(context,addr))
245                 interrupt_handle_now(signal, info, context);
246     }
247 }
248 #endif
249
250 void sigcont_handler(int signal, siginfo_t *info, void *void_context)
251 {
252     /* We need to have a handler installed for this signal so that
253      * sigwaitinfo() for it actually returns at the appropriate time.
254      * We don't need it to actually do anything.  This mkes it
255      * possibly the only signal handler in SBCL that doesn't depend on
256      * not-guaranteed-by-POSIX features 
257      */    
258 }
259
260 void
261 os_install_interrupt_handlers(void)
262 {
263     undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT,
264                                                  sigsegv_handler);
265 #ifdef LISP_FEATURE_SB_THREAD
266     undoably_install_low_level_interrupt_handler(SIG_INTERRUPT_THREAD,
267                                                  handle_rt_signal);
268     undoably_install_low_level_interrupt_handler(SIG_STOP_FOR_GC,
269                                                  sig_stop_for_gc_handler);
270 #endif
271     undoably_install_low_level_interrupt_handler(SIG_DEQUEUE,
272                                                  sigcont_handler);
273 }
274