0.6.9.2:
[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 "./signal.h"
25 #include "os.h"
26 #include "arch.h"
27 #include "globals.h"
28 #include "interrupt.h"
29 #include "lispregs.h"
30 #include "sbcl.h"
31
32 #include <sys/types.h>
33 #include <signal.h>
34 /* #include <sys/sysinfo.h> */
35 #include <sys/proc.h>
36 #include "validate.h"
37 vm_size_t os_vm_page_size;
38
39 #if defined GENCGC
40 #include "gencgc.h"
41 #endif
42
43 /* The different BSD variants have diverged in exactly where they
44  * store signal context information, but at least they tend to use the
45  * same stems to name the structure fields, so by using this macro we
46  * can share a fair amount of code between different variants. */
47 #if defined __FreeBSD__
48 #define CONTEXT_ADDR_FROM_STEM(stem) &context->uc_mcontext.mc_ ## stem
49 #elif defined __OpenBSD__
50 #define CONTEXT_ADDR_FROM_STEM(stem) &context->sc_ ## stem
51 #else
52 #error unsupported BSD variant
53 #endif
54 \f
55 void
56 os_init(void)
57 {
58     os_vm_page_size = getpagesize();
59 }
60
61 /* KLUDGE: There is strong family resemblance in the signal context
62  * stuff in FreeBSD and OpenBSD, but in detail they're different in
63  * almost every line of code. It would be nice to find some way to
64  * factor out the commonality better; failing that, it might be best
65  * just to split this generic-BSD code into one variant for each BSD. */
66    
67 int *
68 os_context_register_addr(os_context_t *context, int offset)
69 {
70     switch(offset) {
71     case  0:
72         return CONTEXT_ADDR_FROM_STEM(eax);
73     case  2:
74         return CONTEXT_ADDR_FROM_STEM(ecx);
75     case  4:
76         return CONTEXT_ADDR_FROM_STEM(edx);
77     case  6:
78         return CONTEXT_ADDR_FROM_STEM(ebx);
79     case  8:
80         return CONTEXT_ADDR_FROM_STEM(esp);
81     case 10:
82         return CONTEXT_ADDR_FROM_STEM(ebp);
83     case 12:
84         return CONTEXT_ADDR_FROM_STEM(esi);
85     case 14:
86         return CONTEXT_ADDR_FROM_STEM(edi);
87     default:
88         return 0;
89     }
90 }
91
92 int *
93 os_context_pc_addr(os_context_t *context)
94 {
95 #if defined __FreeBSD__
96     return CONTEXT_ADDR_FROM_STEM(eip);
97 #elif defined __OpenBSD__
98     return CONTEXT_ADDR_FROM_STEM(pc);
99 #else
100 #error unsupported BSD variant
101 #endif
102 }
103
104 int *
105 os_context_sp_addr(os_context_t *context)
106 {
107     return CONTEXT_ADDR_FROM_STEM(esp);
108 }
109
110 sigset_t *
111 os_context_sigmask_addr(os_context_t *context)
112 {
113     /* (Unlike most of the other context fields that we access, the
114      * signal mask field is a field of the basic, outermost context
115      * struct itself both in FreeBSD 4.0 and in OpenBSD 2.6.) */
116 #if defined __FreeBSD__
117     return &context->uc_sigmask;
118 #elif defined __OpenBSD__
119     return &context->sc_mask;
120 #else
121 #error unsupported BSD variant
122 #endif
123 }
124
125 os_vm_address_t
126 os_validate(os_vm_address_t addr, os_vm_size_t len)
127 {
128     int flags = MAP_PRIVATE | MAP_ANON;
129
130     if (addr)
131         flags |= MAP_FIXED;
132
133     addr = mmap(addr, len, OS_VM_PROT_ALL, flags, -1, 0);
134
135     if (addr == MAP_FAILED) {
136         perror("mmap");
137         return NULL;
138     }
139
140     return addr;
141 }
142
143 void
144 os_invalidate(os_vm_address_t addr, os_vm_size_t len)
145 {
146     if (munmap(addr, len) == -1)
147         perror("munmap");
148 }
149
150 os_vm_address_t
151 os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len)
152 {
153     addr = mmap(addr, len,
154                 OS_VM_PROT_ALL,
155                 MAP_PRIVATE | MAP_FILE | MAP_FIXED,
156                 fd, (off_t) offset);
157
158     if (addr == MAP_FAILED) {
159         perror("mmap");
160         lose("unexpected mmap(..) failure");
161     }
162
163     return addr;
164 }
165
166 void
167 os_flush_icache(os_vm_address_t address, os_vm_size_t length)
168 {
169 }
170
171 void
172 os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot)
173 {
174     if (mprotect(address, length, prot) == -1) {
175         perror("mprotect");
176     }
177 }
178 \f
179 static boolean
180 in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen)
181 {
182     char* beg = (char*) sbeg;
183     char* end = (char*) sbeg + slen;
184     char* adr = (char*) a;
185     return (adr >= beg && adr < end);
186 }
187
188 boolean
189 is_valid_lisp_addr(os_vm_address_t addr)
190 {
191     return in_range_p(addr, READ_ONLY_SPACE_START, READ_ONLY_SPACE_SIZE)
192         || in_range_p(addr, STATIC_SPACE_START   , STATIC_SPACE_SIZE   )
193         || in_range_p(addr, DYNAMIC_SPACE_START  , DYNAMIC_SPACE_SIZE  )
194         || in_range_p(addr, CONTROL_STACK_START  , CONTROL_STACK_SIZE  )
195         || in_range_p(addr, BINDING_STACK_START  , BINDING_STACK_SIZE  );
196 }
197 \f
198 /*
199  * any OS-dependent special low-level handling for signals
200  */
201
202 #if !defined GENCGC
203
204 void
205 os_install_interrupt_handlers(void)
206 {}
207
208 #else
209
210 /*
211  * The GENCGC needs to be hooked into whatever signal is raised for
212  * page fault on this OS.
213  */
214 static void
215 memory_fault_handler(int signal, siginfo_t *siginfo, void *void_context)
216 {
217     /* The way that we extract low level information like the fault
218      * address is not specified by POSIX. */
219 #if defined __FreeBSD__
220     void *fault_addr = siginfo->si_addr;
221 #elif defined __OpenBSD__
222     void *fault_addr = siginfo->si_addr;
223 #else
224 #error unsupported BSD variant
225 #endif
226     if (!gencgc_handle_wp_violation(fault_addr)) {
227         interrupt_handle_now(signal, siginfo, void_context);
228     }
229 }
230 void
231 os_install_interrupt_handlers(void)
232 {
233 #if defined __FreeBSD__
234     interrupt_install_low_level_handler(SIGBUS, memory_fault_handler);
235 #elif defined __OpenBSD__
236     interrupt_install_low_level_handler(SIGSEGV, memory_fault_handler);
237 #else
238 #error unsupported BSD variant
239 #endif
240 }
241
242 #endif /* !defined GENCGC */