6372c42fd5548373afe121915dde03181d03f709
[sbcl.git] / src / runtime / bsd-os.c
1 /*
2  * OS-dependent routines for BSD-ish systems
3  *
4  * This file (along with os.h) exports an OS-independent interface to
5  * the operating system VM facilities. This interface looks a lot like
6  * the Mach interface (but simpler in some places). For some operating
7  * systems, a subset of these functions will have to be emulated.
8  */
9
10 /*
11  * This software is part of the SBCL system. See the README file for
12  * more information.
13  *
14  * This software is derived from the CMU CL system, which was
15  * written at Carnegie Mellon University and released into the
16  * public domain. The software is in the public domain and is
17  * provided with absolutely no warranty. See the COPYING and CREDITS
18  * files for more information.
19  */
20
21 #include <stdio.h>
22 #include <sys/param.h>
23 #include <sys/file.h>
24 #include <unistd.h>
25 #include <assert.h>
26 #include "sbcl.h"
27 #include "./signal.h"
28 #include "os.h"
29 #include "arch.h"
30 #include "globals.h"
31 #include "interrupt.h"
32 #include "interr.h"
33 #include "lispregs.h"
34 #include "thread.h"
35 #include "runtime.h"
36 #include "genesis/static-symbols.h"
37 #include "genesis/fdefn.h"
38
39 #include <sys/types.h>
40 #include <signal.h>
41 /* #include <sys/sysinfo.h> */
42 #include "validate.h"
43 \f
44 os_vm_size_t os_vm_page_size;
45
46 #ifdef __NetBSD__
47 #include <sys/resource.h>
48 #include <sys/sysctl.h>
49 #include <string.h>
50
51 static void netbsd_init();
52 #endif /* __NetBSD__ */
53
54 #ifdef __FreeBSD__
55 #include <sys/sysctl.h>
56 #include <osreldate.h>
57
58 static void freebsd_init();
59 #endif /* __FreeBSD__ */
60
61 #if defined(LISP_FEATURE_DARWIN) && defined(LISP_FEATURE_X86)
62 static void x86_darwin_init();
63 #endif
64
65 void
66 os_init(char *argv[], char *envp[])
67 {
68     os_vm_page_size = getpagesize();
69
70 #ifdef __NetBSD__
71     netbsd_init();
72 #endif /* __NetBSD__ */
73 #ifdef __FreeBSD__
74     freebsd_init();
75 #endif /* __FreeBSD__ */
76 #if defined(LISP_FEATURE_DARWIN) && defined(LISP_FEATURE_X86)
77     x86_darwin_init();
78 #endif
79 }
80
81 int *os_context_pc_addr(os_context_t *context)
82 {
83 #if defined __FreeBSD__
84     return CONTEXT_ADDR_FROM_STEM(eip);
85 #elif defined __OpenBSD__
86     return CONTEXT_ADDR_FROM_STEM(pc);
87 #elif defined __NetBSD__
88     return CONTEXT_ADDR_FROM_STEM(EIP);
89 #elif defined(LISP_FEATURE_DARWIN) && defined(LISP_FEATURE_X86)
90     return CONTEXT_ADDR_FROM_STEM(eip);
91 #elif defined LISP_FEATURE_DARWIN
92     return &context->uc_mcontext->ss.srr0;
93 #else
94 #error unsupported BSD variant
95 #endif
96 }
97
98 sigset_t *
99 os_context_sigmask_addr(os_context_t *context)
100 {
101     /* (Unlike most of the other context fields that we access, the
102      * signal mask field is a field of the basic, outermost context
103      * struct itself both in FreeBSD 4.0 and in OpenBSD 2.6.) */
104 #if defined(__FreeBSD__)  || defined(__NetBSD__) || defined(LISP_FEATURE_DARWIN)
105     return &context->uc_sigmask;
106 #elif defined (__OpenBSD__)
107     return &context->sc_mask;
108 #else
109 #error unsupported BSD variant
110 #endif
111 }
112
113 os_vm_address_t
114 os_validate(os_vm_address_t addr, os_vm_size_t len)
115 {
116     int flags = MAP_PRIVATE | MAP_ANON;
117
118     if (addr)
119         flags |= MAP_FIXED;
120
121     addr = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);
122
123     if (addr == MAP_FAILED) {
124         perror("mmap");
125         return NULL;
126     }
127
128     return addr;
129 }
130
131 void
132 os_invalidate(os_vm_address_t addr, os_vm_size_t len)
133 {
134     if (munmap(addr, len) == -1)
135         perror("munmap");
136 }
137
138 os_vm_address_t
139 os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len)
140 {
141     addr = mmap(addr, len,
142                 OS_VM_PROT_ALL,
143                 MAP_PRIVATE | MAP_FILE | MAP_FIXED,
144                 fd, (off_t) offset);
145
146     if (addr == MAP_FAILED) {
147         perror("mmap");
148         lose("unexpected mmap(..) failure\n");
149     }
150
151     return addr;
152 }
153
154 void
155 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
156 {
157     if (mprotect(address, length, prot) == -1) {
158         perror("mprotect");
159     }
160 }
161 \f
162 static boolean
163 in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
164 {
165     char* beg = (char*) sbeg;
166     char* end = (char*) sbeg + slen;
167     char* adr = (char*) a;
168     return (adr >= beg && adr < end);
169 }
170
171 boolean
172 is_valid_lisp_addr(os_vm_address_t addr)
173 {
174     struct thread *th;
175     if(in_range_p(addr, READ_ONLY_SPACE_START, READ_ONLY_SPACE_SIZE) ||
176        in_range_p(addr, STATIC_SPACE_START   , STATIC_SPACE_SIZE) ||
177        in_range_p(addr, DYNAMIC_SPACE_START  , DYNAMIC_SPACE_SIZE))
178         return 1;
179     for_each_thread(th) {
180         if((th->control_stack_start <= addr) && (addr < th->control_stack_end))
181             return 1;
182         if(in_range_p(addr, th->binding_stack_start, BINDING_STACK_SIZE))
183             return 1;
184     }
185     return 0;
186 }
187 \f
188 /*
189  * any OS-dependent special low-level handling for signals
190  */
191
192 #if defined LISP_FEATURE_GENCGC
193
194 /*
195  * The GENCGC needs to be hooked into whatever signal is raised for
196  * page fault on this OS.
197  */
198 static void
199 memory_fault_handler(int signal, siginfo_t *siginfo, void *void_context)
200 {
201     os_context_t *context = arch_os_get_context(&void_context);
202     void *fault_addr = arch_get_bad_addr(signal, siginfo, context);
203
204 #if defined(MEMORY_FAULT_DEBUG)
205     fprintf(stderr, "Memory fault at: %p, PC: %x\n", fault_addr, *os_context_pc_addr(context));
206 #if defined(ARCH_HAS_STACK_POINTER)
207     fprintf(stderr, "Stack pointer: %x\n", *os_context_sp_addr(context));
208 #endif
209 #endif
210
211     if (!gencgc_handle_wp_violation(fault_addr))
212         if(!handle_guard_page_triggered(context,fault_addr)) {
213 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
214             arrange_return_to_lisp_function(context, SymbolFunction(MEMORY_FAULT_ERROR));
215 #else
216             if (!interrupt_maybe_gc_int(signal, siginfo, context)) {
217                 interrupt_handle_now(signal, siginfo, context);
218             }
219 #if defined(LISP_FEATURE_DARWIN)
220             /* Work around G5 bug; fix courtesy gbyers */
221             DARWIN_FIX_CONTEXT(context);
222 #endif
223 #endif
224         }
225 }
226
227 void
228 os_install_interrupt_handlers(void)
229 {
230     SHOW("os_install_interrupt_handlers()/bsd-os/defined(GENCGC)");
231     undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT,
232                                                  memory_fault_handler);
233 #ifdef SIG_MEMORY_FAULT2
234     undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT2,
235                                                  memory_fault_handler);
236 #endif
237     SHOW("leaving os_install_interrupt_handlers()");
238 }
239
240 #else /* Currently Darwin only */
241
242 static void
243 sigsegv_handler(int signal, siginfo_t *info, void* void_context)
244 {
245     os_context_t *context = arch_os_get_context(&void_context);
246     unsigned int pc =  (unsigned int *)(*os_context_pc_addr(context));
247     os_vm_address_t addr;
248
249     addr = arch_get_bad_addr(signal,info,context);
250     if(!interrupt_maybe_gc(signal, info, context))
251         if(!handle_guard_page_triggered(context,addr))
252             interrupt_handle_now(signal, info, context);
253     /* Work around G5 bug; fix courtesy gbyers */
254     DARWIN_FIX_CONTEXT(context);
255 }
256
257 void
258 os_install_interrupt_handlers(void)
259 {
260     SHOW("os_install_interrupt_handlers()/bsd-os/!defined(GENCGC)");
261     undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT,
262                                                  sigsegv_handler);
263 #ifdef SIG_MEMORY_FAULT2
264     undoably_install_low_level_interrupt_handler(SIG_MEMORY_FAULT2,
265                                                  sigsegv_handler);
266 #endif
267 }
268
269 #endif /* defined GENCGC */
270
271 #ifdef __NetBSD__
272 static void netbsd_init()
273 {
274     struct rlimit rl;
275     int mib[2], osrev;
276     size_t len;
277
278     /* Are we running on a sufficiently functional kernel? */
279     mib[0] = CTL_KERN;
280     mib[1] = KERN_OSREV;
281
282     len = sizeof(osrev);
283     sysctl(mib, 2, &osrev, &len, NULL, 0);
284
285     /* If we're older than 2.0... */
286     if (osrev < 200000000) {
287         fprintf(stderr, "osrev = %d (needed at least 200000000).\n", osrev);
288         lose("NetBSD kernel too old to run sbcl.\n");
289     }
290
291     /* NetBSD counts mmap()ed space against the process's data size limit,
292      * so yank it up. This might be a nasty thing to do? */
293     getrlimit (RLIMIT_DATA, &rl);
294     /* Amazingly for such a new port, the provenance and meaning of
295        this number are unknown.  It might just mean REALLY_BIG_LIMIT,
296        or possibly it should be calculated from dynamic space size.
297        -- CSR, 2004-04-08 */
298     rl.rlim_cur = 1073741824;
299     if (setrlimit (RLIMIT_DATA, &rl) < 0) {
300         fprintf (stderr,
301                  "RUNTIME WARNING: unable to raise process data size limit:\n\
302   %s.\n\
303 The system may fail to start.\n",
304                  strerror(errno));
305     }
306 }
307 #endif /* __NetBSD__ */
308
309 #ifdef __FreeBSD__
310 static void freebsd_init()
311 {
312     /* Quote from sbcl-devel (NIIMI Satoshi): "Some OSes, like FreeBSD
313      * 4.x with GENERIC kernel, does not enable SSE support even on
314      * SSE capable CPUs". Detect this situation and skip the
315      * fast_bzero sse/base selection logic that's normally done in
316      * x86-assem.S.
317      */
318 #ifdef LISP_FEATURE_X86
319     size_t len;
320     int instruction_sse;
321
322     len = sizeof(instruction_sse);
323     if (sysctlbyname("hw.instruction_sse", &instruction_sse, &len, NULL, 0) == 0
324         && instruction_sse != 0) {
325         /* Use the SSE detector */
326         fast_bzero_pointer = fast_bzero_detect;
327     }
328 #endif /* LISP_FEATURE_X86 */
329 }
330 #endif /* __FreeBSD__ */
331 \f
332 /* threads */
333
334 /* no threading in any *BSD variant on any CPU (yet? in sbcl-0.8.0 anyway) */
335 #ifdef LISP_FEATURE_SB_THREAD
336 #error "Define threading support functions"
337 #else
338 int arch_os_thread_init(struct thread *thread) {
339   stack_t sigstack;
340 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
341     /* Signal handlers are run on the control stack, so if it is exhausted
342      * we had better use an alternate stack for whatever signal tells us
343      * we've exhausted it */
344     sigstack.ss_sp=((void *) thread)+dynamic_values_bytes;
345     sigstack.ss_flags=0;
346     sigstack.ss_size = 32*SIGSTKSZ;
347     sigaltstack(&sigstack,0);
348 #endif
349     return 1;                  /* success */
350 }
351 int arch_os_thread_cleanup(struct thread *thread) {
352     return 1;                  /* success */
353 }
354 #endif
355
356 #if defined(LISP_FEATURE_DARWIN) && defined(LISP_FEATURE_X86)
357 static void x86_darwin_init()
358 {
359     struct sigaltstack sigstack;
360     sigstack.ss_sp = os_allocate(32*SIGSTKSZ);
361     if (sigstack.ss_sp) {
362         sigstack.ss_flags=0;
363         sigstack.ss_size = 32*SIGSTKSZ;
364         sigaltstack(&sigstack,0);
365     }
366 }
367 #endif
368
369 #ifdef LISP_FEATURE_DARWIN
370 /* defined in ppc-darwin-os.c instead */
371 #elif defined(LISP_FEATURE_FREEBSD)
372 #ifndef KERN_PROC_PATHNAME
373 #define KERN_PROC_PATHNAME 12
374 #endif
375
376 char *
377 os_get_runtime_executable_path()
378 {
379     char path[PATH_MAX + 1];
380
381     if (getosreldate() >= 600024) {
382         /* KERN_PROC_PATHNAME is available */
383         size_t len = PATH_MAX + 1;
384         int mib[4];
385
386         mib[0] = CTL_KERN;
387         mib[1] = KERN_PROC;
388         mib[2] = KERN_PROC_PATHNAME;
389         mib[3] = -1;
390         if (sysctl(mib, 4, &path, &len, NULL, 0) != 0)
391             return NULL;
392     } else {
393         int size;
394         size = readlink("/proc/curproc/file", path, sizeof(path) - 1);
395         if (size < 0)
396             return NULL;
397         path[size] = '\0';
398     }
399     if (strcmp(path, "unknown") == 0)
400         return NULL;
401     return copied_string(path);
402 }
403 #else /* Not DARWIN or FREEBSD */
404 char *
405 os_get_runtime_executable_path()
406 {
407     return NULL;
408 }
409 #endif