| Commit | Line | Data |
|---|---|---|
| b39b25d0 | 1 | /* |
| 2 | * NetBSD-os.c. | |
| 3 | * From OpenBSD-os.c 1.1 2001/12/06 19:15:44 pmai Exp | |
| 4 | * From FreeBSD-os.c 1.6 2000/10/24 13:32:30 dtc Exp | |
| 5 | * | |
| 6 | * OS-dependent routines. This file (along with os.h) exports an | |
| 7 | * OS-independent interface to the operating system VM facilities. | |
| 8 | * Suprisingly, this interface looks a lot like the Mach interface | |
| 9 | * (but simpler in some places). For some operating systems, a subset | |
| 10 | * of these functions will have to be emulated. | |
| 11 | * | |
| 12 | * This is the OSF1 version. By Sean Hallgren. | |
| 13 | * Much hacked by Paul Werkowski | |
| 14 | * GENCGC support by Douglas Crosher, 1996, 1997. | |
| 15 | * Frobbed for OpenBSD by Pierre R. Mai, 2001. | |
| 16 | * Frobbed for NetBSD by Pierre R. Mai, 2002. | |
| 17 | * | |
| b39b25d0 | 18 | * |
| 19 | */ | |
| 20 | ||
| 21 | #include <stdio.h> | |
| 22 | #include <stdlib.h> | |
| 23 | #include <sys/param.h> | |
| 24 | #include <sys/file.h> | |
| 25 | #include <errno.h> | |
| b39b25d0 | 26 | #include "os.h" |
| 27 | #include "arch.h" | |
| 28 | #include "globals.h" | |
| 29 | #include "interrupt.h" | |
| 30 | #include "lispregs.h" | |
| 31 | #include "internals.h" | |
| 32 | ||
| 33 | #include <sys/types.h> | |
| 34 | #include <signal.h> | |
| 35 | /* #include <sys/sysinfo.h> */ | |
| 36 | #include <sys/proc.h> | |
| 5d2cd5df | 37 | #include <dlfcn.h> |
| aa9c2237 | 38 | #include <sys/sysctl.h> |
| b39b25d0 | 39 | #include "validate.h" |
| 40 | size_t os_vm_page_size; | |
| 41 | ||
| b39b25d0 | 42 | #if defined GENCGC |
| 43 | #include "gencgc.h" | |
| 44 | #endif | |
| b39b25d0 | 45 | \f |
| 9a8c1c2f | 46 | |
| 47 | void | |
| dafb9e03 | 48 | os_init0(const char *argv[], const char *envp[]) |
| 49 | {} | |
| 50 | ||
| 51 | void | |
| 0f0aed07 | 52 | os_init(const char *argv[], const char *envp[]) |
| b39b25d0 | 53 | { |
| 9a8c1c2f | 54 | os_vm_page_size = OS_VM_DEFAULT_PAGESIZE; |
| b39b25d0 | 55 | } |
| 56 | ||
| daa432a5 | 57 | unsigned long * |
| 58 | os_sigcontext_reg(ucontext_t *scp, int index) | |
| b39b25d0 | 59 | { |
| 5d2cd5df | 60 | #ifdef i386 |
| daa432a5 | 61 | switch (index) { |
| 62 | case 0: | |
| 63 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EAX]; | |
| 64 | case 2: | |
| 65 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_ECX]; | |
| 66 | case 4: | |
| 67 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EDX]; | |
| 68 | case 6: | |
| 69 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EBX]; | |
| 70 | case 8: | |
| 71 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_ESP]; | |
| 72 | case 10: | |
| 73 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EBP]; | |
| 74 | case 12: | |
| 75 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_ESI]; | |
| 76 | case 14: | |
| 77 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EDI]; | |
| 5d2cd5df | 78 | } |
| 5d2cd5df | 79 | #endif |
| daa432a5 | 80 | return NULL; |
| 81 | } | |
| 82 | ||
| 83 | unsigned long * | |
| 84 | os_sigcontext_pc(ucontext_t *scp) | |
| 85 | { | |
| 86 | #ifdef i386 | |
| 87 | return (unsigned long *) &scp->uc_mcontext.__gregs[_REG_EIP]; | |
| 88 | #endif | |
| b39b25d0 | 89 | } |
| 90 | ||
| 7fbce9e3 | 91 | unsigned char * |
| 92 | os_sigcontext_fpu_reg(ucontext_t *scp, int index) | |
| 93 | { | |
| 94 | unsigned char *reg = NULL; | |
| 95 | ||
| 9cf15715 | 96 | DPRINTF(0, (stderr, "fpu reg index = %d\n", index)); |
| 97 | ||
| 7fbce9e3 | 98 | if (scp->uc_flags & _UC_FPU) { |
| 99 | if (scp->uc_flags & _UC_FXSAVE) { | |
| eecd2cbb | 100 | /* |
| 101 | * fp_xmm is an array of bytes in the format of the FXSAVE | |
| 102 | * instruction. The x87 registers are at offset 32 from | |
| 103 | * the start and each entry takes 16 bytes (only 10 | |
| 104 | * needed). The XMM registers are at offset 160 from the | |
| 105 | * start of the array, and each XMM register is 16 bytes | |
| 106 | * long. | |
| 107 | */ | |
| 9cf15715 | 108 | if (index >= 8) { |
| eecd2cbb | 109 | reg = &scp->uc_mcontext.__fpregs.__fp_reg_set.__fp_xmm_state.__fp_xmm[160 + 16*(index - 8)]; |
| 9cf15715 | 110 | DPRINTF(0, (stderr, " sse2 = %g\n", (double) *(double*) reg)); |
| eecd2cbb | 111 | } else { |
| 112 | reg = &scp->uc_mcontext.__fpregs.__fp_reg_set.__fp_xmm_state.__fp_xmm[32 + 16*index]; | |
| 9cf15715 | 113 | DPRINTF(0, (stderr, " sse2 x87 = %g\n", (double) *(long double*) reg)); |
| eecd2cbb | 114 | } |
| 115 | ||
| 7fbce9e3 | 116 | } else { |
| eecd2cbb | 117 | /* |
| 118 | * In this case, we have the FNSAVE format. The x87 | |
| 119 | * registers are located at offset 28 and take 10 bytes | |
| 120 | * each. | |
| 121 | */ | |
| 122 | reg = &scp->uc_mcontext.__fpregs.__fp_reg_set.__fpchip_state.__fp_state[28 + 10*index]; | |
| 9cf15715 | 123 | DPRINTF(0, (stderr, " x87 = %g\n", (double) *(long double*) reg)); |
| 7fbce9e3 | 124 | } |
| 125 | } else { | |
| 126 | reg = NULL; | |
| 127 | } | |
| 128 | return reg; | |
| 129 | } | |
| 130 | ||
| 131 | unsigned int | |
| 132 | os_sigcontext_fpu_modes(ucontext_t *scp) | |
| 133 | { | |
| 134 | unsigned int modes; | |
| 135 | ||
| 136 | union savefpu *sv = (union savefpu *) &scp->uc_mcontext.__fpregs.__fp_reg_set; | |
| 137 | struct env87 *env_87 = (struct env87 *) &sv->sv_87.sv_env; | |
| 138 | struct envxmm *env_xmm = (struct envxmm *) &sv->sv_xmm.sv_env; | |
| 139 | u_int16_t cw; | |
| 140 | u_int16_t sw; | |
| 141 | ||
| 142 | if (scp->uc_flags & _UC_FPU) { | |
| 143 | if (scp->uc_flags & _UC_FXSAVE) { | |
| 144 | cw = env_xmm->en_cw; | |
| 145 | sw = env_xmm->en_sw; | |
| 146 | } else { | |
| 147 | cw = env_87->en_cw & 0xffff; | |
| 148 | sw = env_87->en_sw & 0xffff; | |
| 149 | } | |
| 150 | } else { | |
| 151 | cw = 0; | |
| 152 | sw = 0x3f; | |
| 153 | } | |
| 154 | ||
| 155 | modes = ((cw & 0x3f) << 7) | (sw & 0x3f); | |
| 156 | ||
| 157 | #ifdef FEATURE_SSE2 | |
| 158 | if (fpu_mode == SSE2) { | |
| 159 | u_int32_t mxcsr = env_xmm->en_mxcsr; | |
| 160 | ||
| 161 | DPRINTF(0, (stderr, "SSE2 modes = %08x\n", (int)mxcsr)); | |
| 162 | modes |= mxcsr; | |
| 163 | } | |
| 164 | #endif | |
| 165 | modes ^= (0x3f << 7); | |
| 166 | return modes; | |
| 167 | } | |
| 168 | ||
| 9a8c1c2f | 169 | os_vm_address_t |
| 170 | os_validate(os_vm_address_t addr, os_vm_size_t len) | |
| b39b25d0 | 171 | { |
| 1e078503 | 172 | int flags = MAP_PRIVATE | MAP_ANON; |
| 9a8c1c2f | 173 | |
| 174 | /* | |
| 175 | * NetBSD 1.5.2 seems to insist on each mmap being less than 128MB. | |
| daa432a5 | 176 | * So we mmap in 64MB steps. |
| 9a8c1c2f | 177 | */ |
| 178 | ||
| 179 | if (addr) | |
| 180 | flags |= MAP_FIXED; | |
| 9a8c1c2f | 181 | |
| 182 | DPRINTF(0, (stderr, "os_validate %p %d =>", addr, len)); | |
| 183 | ||
| 184 | if (addr) { | |
| 185 | os_vm_address_t curaddr = addr; | |
| 186 | ||
| 187 | while (len > 0) { | |
| 188 | os_vm_address_t resaddr; | |
| 189 | int curlen = MIN(64 * 1024 * 1024, len); | |
| 190 | ||
| 191 | resaddr = mmap(curaddr, curlen, OS_VM_PROT_ALL, flags, -1, 0); | |
| 192 | ||
| 193 | if (resaddr == (os_vm_address_t) - 1) { | |
| 194 | perror("mmap"); | |
| 195 | ||
| 196 | while (curaddr > addr) { | |
| 197 | curaddr -= 64 * 1024 * 1024; | |
| 198 | munmap(curaddr, 64 * 1024 * 1024); | |
| b39b25d0 | 199 | } |
| 9a8c1c2f | 200 | |
| 201 | return NULL; | |
| b39b25d0 | 202 | } |
| 203 | ||
| 9a8c1c2f | 204 | DPRINTF(0, (stderr, " %p", resaddr)); |
| 205 | ||
| 206 | curaddr += curlen; | |
| 207 | len -= curlen; | |
| b39b25d0 | 208 | } |
| 9a8c1c2f | 209 | |
| 210 | DPRINTF(0, (stderr, "\n")); | |
| 211 | } else { | |
| 212 | addr = mmap(0, len, OS_VM_PROT_ALL, flags, -1, 0); | |
| 213 | ||
| 214 | if (addr == (os_vm_address_t) - 1) { | |
| 215 | perror("mmap"); | |
| 216 | return NULL; | |
| b39b25d0 | 217 | } |
| 9a8c1c2f | 218 | |
| 219 | DPRINTF(0, (stderr, " %p\n", addr)); | |
| b39b25d0 | 220 | } |
| 221 | ||
| 9a8c1c2f | 222 | return addr; |
| b39b25d0 | 223 | } |
| 224 | ||
| 9a8c1c2f | 225 | void |
| 226 | os_invalidate(os_vm_address_t addr, os_vm_size_t len) | |
| b39b25d0 | 227 | { |
| 9a8c1c2f | 228 | DPRINTF(0, (stderr, "os_invalidate %p %d\n", addr, len)); |
| b39b25d0 | 229 | |
| 9a8c1c2f | 230 | if (munmap(addr, len) == -1) |
| 231 | perror("munmap"); | |
| b39b25d0 | 232 | } |
| 233 | ||
| 9a8c1c2f | 234 | os_vm_address_t |
| 235 | os_map(int fd, int offset, os_vm_address_t addr, os_vm_size_t len) | |
| b39b25d0 | 236 | { |
| 9a8c1c2f | 237 | addr = mmap(addr, len, |
| 238 | OS_VM_PROT_ALL, | |
| 239 | MAP_PRIVATE | MAP_FILE | MAP_FIXED, fd, (off_t) offset); | |
| b39b25d0 | 240 | |
| 9a8c1c2f | 241 | if (addr == (os_vm_address_t) - 1) |
| 242 | perror("mmap"); | |
| b39b25d0 | 243 | |
| 9a8c1c2f | 244 | return addr; |
| b39b25d0 | 245 | } |
| 246 | ||
| 9a8c1c2f | 247 | void |
| 248 | os_flush_icache(os_vm_address_t address, os_vm_size_t length) | |
| b39b25d0 | 249 | { |
| 250 | } | |
| 251 | ||
| 9a8c1c2f | 252 | void |
| 253 | os_protect(os_vm_address_t address, os_vm_size_t length, os_vm_prot_t prot) | |
| b39b25d0 | 254 | { |
| 9a8c1c2f | 255 | if (mprotect(address, length, prot) == -1) |
| 256 | perror("mprotect"); | |
| b39b25d0 | 257 | } |
| b39b25d0 | 258 | \f |
| 259 | ||
| 9a8c1c2f | 260 | |
| 261 | static boolean | |
| 262 | in_range_p(os_vm_address_t a, lispobj sbeg, size_t slen) | |
| b39b25d0 | 263 | { |
| 9a8c1c2f | 264 | char *beg = (char *) sbeg; |
| 265 | char *end = (char *) sbeg + slen; | |
| 266 | char *adr = (char *) a; | |
| 267 | ||
| 268 | return (adr >= beg && adr < end); | |
| b39b25d0 | 269 | } |
| 270 | ||
| 9a8c1c2f | 271 | boolean |
| 272 | valid_addr(os_vm_address_t addr) | |
| b39b25d0 | 273 | { |
| 9a8c1c2f | 274 | os_vm_address_t newaddr; |
| b39b25d0 | 275 | |
| 9a8c1c2f | 276 | newaddr = os_trunc_to_page(addr); |
| 277 | ||
| 44a8f0c7 RT |
278 | if (in_range_p(addr, READ_ONLY_SPACE_START, read_only_space_size) |
| 279 | || in_range_p(addr, STATIC_SPACE_START, static_space_size) | |
| 9a8c1c2f | 280 | || in_range_p(addr, DYNAMIC_0_SPACE_START, dynamic_space_size) |
| 5d2cd5df | 281 | #ifndef GENCGC |
| 9a8c1c2f | 282 | || in_range_p(addr, DYNAMIC_1_SPACE_START, dynamic_space_size) |
| 5d2cd5df | 283 | #endif |
| 44a8f0c7 RT |
284 | || in_range_p(addr, CONTROL_STACK_START, control_stack_size) |
| 285 | || in_range_p(addr, BINDING_STACK_START, binding_stack_size)) | |
| 9a8c1c2f | 286 | return TRUE; |
| 287 | return FALSE; | |
| b39b25d0 | 288 | } |
| b39b25d0 | 289 | \f |
| 9a8c1c2f | 290 | |
| 291 | static void | |
| 292 | sigsegv_handler(HANDLER_ARGS) | |
| b39b25d0 | 293 | { |
| 5d2cd5df | 294 | #if defined GENCGC |
| 88a6f6e5 | 295 | #if SIGSEGV_VERBOSE |
| 9a8c1c2f | 296 | caddr_t fault_addr = code ? code->si_addr : 0; |
| b39b25d0 | 297 | |
| 9a8c1c2f | 298 | fprintf(stderr, "Signal %d, fault_addr=%p, page_index=%d:\n", |
| 299 | signal, fault_addr, page_index); | |
| 5d2cd5df | 300 | #endif |
| 301 | ||
| 97083c55 | 302 | if (gc_write_barrier(code->si_addr)) |
| 9a8c1c2f | 303 | return; |
| 5d2cd5df | 304 | #endif |
| b39b25d0 | 305 | |
| 9a8c1c2f | 306 | SAVE_CONTEXT(); |
| b39b25d0 | 307 | |
| 9a8c1c2f | 308 | DPRINTF(0, (stderr, "sigsegv:\n")); |
| 309 | interrupt_handle_now(signal, code, context); | |
| b39b25d0 | 310 | } |
| 311 | ||
| 9a8c1c2f | 312 | static void |
| 313 | sigbus_handler(HANDLER_ARGS) | |
| b39b25d0 | 314 | { |
| 9a8c1c2f | 315 | SAVE_CONTEXT(); |
| b39b25d0 | 316 | |
| 9a8c1c2f | 317 | DPRINTF(0, (stderr, "sigbus:\n")); |
| 318 | interrupt_handle_now(signal, code, context); | |
| b39b25d0 | 319 | } |
| 320 | ||
| 7fbce9e3 | 321 | /* |
| 322 | * Restore the exception flags cleared by the kernel. These bits must | |
| 323 | * be set for Lisp to determine which exception caused the signal. At | |
| 324 | * present, there is no way to distinguish underflow exceptions from | |
| 325 | * denormalized operand exceptions. An underflow exception is assumed | |
| 326 | * if the subcode is FPE_FLTUND. | |
| 327 | */ | |
| 328 | static void | |
| 329 | sigfpe_handler(HANDLER_ARGS) | |
| 330 | { | |
| 331 | ucontext_t *ucontext = (ucontext_t *) context; | |
| 332 | union savefpu *sv = (union savefpu *) &ucontext->uc_mcontext.__fpregs.__fp_reg_set; | |
| 333 | unsigned char trap = 0; | |
| 334 | ||
| 335 | switch (code->si_code) { | |
| 336 | case FPE_FLTDIV: /* ZE */ | |
| 337 | trap = 0x04; | |
| 338 | break; | |
| 339 | case FPE_FLTOVF: /* OE */ | |
| 340 | trap = 0x08; | |
| 341 | break; | |
| 342 | case FPE_FLTUND: /* DE or UE */ | |
| 343 | trap = 0x10; | |
| 344 | break; | |
| 345 | case FPE_FLTRES: /* PE */ | |
| 346 | trap = 0x20; | |
| 347 | break; | |
| 348 | case FPE_FLTINV: /* IE */ | |
| 349 | trap = 0x01; | |
| 350 | break; | |
| 351 | } | |
| 352 | ||
| 353 | if (ucontext->uc_flags & _UC_FXSAVE) { | |
| 354 | sv->sv_xmm.sv_env.en_sw |= trap; | |
| 355 | } else { | |
| 356 | sv->sv_87.sv_env.en_sw |= trap; | |
| 357 | } | |
| 358 | interrupt_handle_now(signal, code, context); | |
| 359 | } | |
| 360 | ||
| 9a8c1c2f | 361 | void |
| 362 | os_install_interrupt_handlers(void) | |
| b39b25d0 | 363 | { |
| 9a8c1c2f | 364 | interrupt_install_low_level_handler(SIGSEGV, sigsegv_handler); |
| 365 | interrupt_install_low_level_handler(SIGBUS, sigbus_handler); | |
| 7fbce9e3 | 366 | interrupt_install_low_level_handler(SIGFPE, sigfpe_handler); |
| b39b25d0 | 367 | } |
| 5d2cd5df | 368 | |
| 369 | void * | |
| 9a8c1c2f | 370 | os_dlsym(const char *sym_name, lispobj lib_list) |
| 5d2cd5df | 371 | { |
| 9a8c1c2f | 372 | if (lib_list != NIL) { |
| 373 | lispobj lib_list_head; | |
| 374 | ||
| 375 | for (lib_list_head = lib_list; | |
| 376 | lib_list_head != NIL; lib_list_head = CONS(lib_list_head)->cdr) { | |
| 377 | struct cons *lib_cons = CONS(CONS(lib_list_head)->car); | |
| 378 | struct sap *dlhandle = (struct sap *) PTR(lib_cons->car); | |
| 379 | void *sym_addr = dlsym((void *) dlhandle->pointer, sym_name); | |
| 380 | ||
| 381 | if (sym_addr) | |
| 382 | return sym_addr; | |
| 5d2cd5df | 383 | } |
| 384 | } | |
| 9a8c1c2f | 385 | |
| 386 | return dlsym(RTLD_DEFAULT, sym_name); | |
| 5d2cd5df | 387 | } |
| 7fbce9e3 | 388 | |
| 389 | void | |
| 390 | restore_fpu(ucontext_t *scp) | |
| 391 | { | |
| 392 | union savefpu *sv = (union savefpu *) &scp->uc_mcontext.__fpregs.__fp_reg_set; | |
| 393 | struct env87 *env_87 = &sv->sv_87.sv_env; | |
| 394 | struct envxmm *env_xmm = &sv->sv_xmm.sv_env; | |
| 395 | u_int16_t cw; | |
| 396 | ||
| 397 | if (scp->uc_flags & _UC_FPU) { | |
| 398 | if (scp->uc_flags & _UC_FXSAVE) { | |
| 399 | cw = env_xmm->en_cw; | |
| 400 | } else { | |
| 401 | cw = env_87->en_cw & 0xffff; | |
| 402 | } | |
| 403 | } else { | |
| 404 | return; | |
| 405 | } | |
| 406 | DPRINTF(0, (stderr, "restore_fpu: cw = %08x\n", (int)cw)); | |
| 407 | __asm__ __volatile__ ("fldcw %0"::"m"(*&cw)); | |
| 408 | ||
| 2c2d669d | 409 | if (fpu_mode == SSE2) { |
| 7fbce9e3 | 410 | u_int32_t mxcsr = env_xmm->en_mxcsr; |
| 411 | ||
| 412 | DPRINTF(0, (stderr, "restore_fpu: mxcsr (raw) = %04x\n", mxcsr)); | |
| 413 | __asm__ __volatile__ ("ldmxcsr %0"::"m"(*&mxcsr)); | |
| 414 | } | |
| 415 | } | |
| aa9c2237 | 416 | |
| 417 | #ifdef i386 | |
| 418 | boolean | |
| 419 | os_support_sse2() | |
| 420 | { | |
| 421 | int support_sse2; | |
| 422 | size_t len; | |
| 423 | ||
| 424 | if (sysctlbyname("machdep.sse2", &support_sse2, &len, | |
| 425 | NULL, 0) == 0 && support_sse2 != 0) | |
| 426 | return TRUE; | |
| 427 | else | |
| 428 | return FALSE; | |
| 429 | } | |
| 430 | #endif |