vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit 218811d066c15fb10060f586b466d91001dcc0bb
parent 51dc3331748d1f24cb610792fa49200e561a15b0
Author: Russ Cox <rsc@swtch.com>
Date:   Sat, 28 Jun 2008 21:36:01 -0400

9vx/OSX: add Mach signal handler, fix up native GUI code

TODO: Perhaps the Mach signal handler belongs in libvx32?

Diffstat:
src/9vx/Makefrag | 8+++++---
src/9vx/a/dat.h | 9+++++++++
src/9vx/a/fns.h | 6++++++
src/9vx/main.c | 13+++++++++++--
src/9vx/osx/screen.c | 66+++++++++++++++++++++++++++++++++++++++++++++++++++---------------
src/9vx/osx/signal.c | 237+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/9vx/sched.c | 17+++++++----------
src/9vx/stub.c | 3---
src/9vx/term.c | 9+++++++++
src/9vx/x11/x11-kernel.c | 6++++++
10 files changed, 341 insertions(+), 33 deletions(-)

diff --git a/src/9vx/Makefrag b/src/9vx/Makefrag @@ -10,9 +10,7 @@ endif ifeq ($(OS),darwin) PLAN9VX=1 -# TODO: osx works except that Carbon gets confused -# that we can survive an EXC_BAD_ACCESS -PLAN9GUI=x11 +PLAN9GUI=osx PLAN9AUDIO=none endif @@ -55,6 +53,10 @@ PLAN9_OBJS = \ vx32.o \ ) +ifeq ($(OS),darwin) +PLAN9_OBJS := $(PLAN9_OBJS) 9vx/osx/signal.o +endif + PLAN9_A_OBJS = \ $(addprefix 9vx/a/, \ allocb.o \ diff --git a/src/9vx/a/dat.h b/src/9vx/a/dat.h @@ -349,3 +349,12 @@ extern int traceprocs; extern int tracesyscalls; extern uchar *uzero; extern int doabort; + +/* Pthreads-based sleep and wakeup. */ +typedef struct Psleep Psleep; +struct Psleep +{ + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + diff --git a/src/9vx/a/fns.h b/src/9vx/a/fns.h @@ -166,3 +166,9 @@ void uartinit(int); void *uvalidaddr(ulong addr, ulong len, int write); int isuaddr(void*); void setsigsegv(int invx32); + +void plock(Psleep*); +void punlock(Psleep*); +void pwakeup(Psleep*); +void psleep(Psleep*); + diff --git a/src/9vx/main.c b/src/9vx/main.c @@ -144,6 +144,7 @@ main(int argc, char **argv) if(username == nil && (username = getuser()) == nil) username = "tor"; + kstrdup(&eve, username); mach0init(); mmuinit(); @@ -155,9 +156,12 @@ main(int argc, char **argv) * fork into the background. This is a little dicey, since we * haven't allocated the screen yet, but that would kick off * a kproc, and we need to fork before we start any pthreads. + * Cannot fork on OS X - it can't handle it. */ +#ifndef __APPLE__ if(!usetty && !nofork && fork() > 0) _exit(0); +#endif /* * Have to do this after fork; on OS X child does @@ -182,6 +186,12 @@ main(int argc, char **argv) } bootinit(); pageinit(); +#ifdef __APPLE__ + if(conf.monitor) + screeninit(); + extern void machsiginit(void); + machsiginit(); +#endif userinit(); terminit(!usetty); uartinit(usetty); @@ -473,7 +483,7 @@ init0(void) touser(sp); /* infinity, and beyond. */ } -static int invx32; +int invx32; /* shared with sbcl-signal.c */ /* * SIGSEGV handler. If we get SIGSEGV while executing @@ -680,7 +690,6 @@ siginit(void) act.sa_flags = SA_SIGINFO; sigaction(SIGUSR1, &act, nil); sigaction(SIGURG, &act, nil); - } void diff --git a/src/9vx/osx/screen.c b/src/9vx/osx/screen.c @@ -54,11 +54,14 @@ enum kWindowFullZoomAttribute }; -static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*); static void screenproc(void*); static void eresized(int); static void fullscreen(int); +static OSStatus quithandler(EventHandlerCallRef, EventRef, void*); +static OSStatus eventhandler(EventHandlerCallRef, EventRef, void*); +static OSStatus cmdhandler(EventHandlerCallRef, EventRef, void*); + enum { CmdFullScreen = 1, @@ -86,7 +89,7 @@ attachscreen(Rectangle *r, ulong *chan, int *depth, } void -screeninit(void) +_screeninit(void) { CGRect cgr; OSXRect or; @@ -131,13 +134,13 @@ screeninit(void) // Set up the clip board. if(PasteboardCreate(kPasteboardClipboard, &osx.snarf) != noErr) panic("pasteboard create"); - + // Explain in great detail which events we want to handle. // Why can't we just have one handler? const EventTypeSpec quits[] = { { kEventClassApplication, kEventAppQuit } }; - const EventTypeSpec commands[] = { + const EventTypeSpec cmds[] = { { kEventClassWindow, kEventWindowClosed }, { kEventClassWindow, kEventWindowBoundsChanged }, { kEventClassCommand, kEventCommandProcess } @@ -152,16 +155,19 @@ screeninit(void) { kEventClassMouse, kEventMouseDragged }, { kEventClassMouse, kEventMouseWheelMoved }, }; + InstallApplicationEventHandler( - NewEventHandlerUPP(eventhandler), + NewEventHandlerUPP(quithandler), nelem(quits), quits, nil, nil); - InstallApplicationEventHandler( - NewEventHandlerUPP(eventhandler), - nelem(events), events, osx.window, nil); + + InstallApplicationEventHandler( + NewEventHandlerUPP(eventhandler), + nelem(events), events, nil, nil); + InstallWindowEventHandler(osx.window, - NewEventHandlerUPP(eventhandler), - nelem(commands), commands, osx.window, nil); - + NewEventHandlerUPP(cmdhandler), + nelem(cmds), cmds, osx.window, nil); + // Finally, put the window on the screen. ShowWindow(osx.window); ShowMenuBar(); @@ -169,34 +175,60 @@ screeninit(void) SelectWindow(osx.window); InitCursor(); +} +static Psleep scr; + +void +screeninit(void) +{ + plock(&scr); kproc("*screen*", screenproc, nil); + while(osx.window == nil) + psleep(&scr); + punlock(&scr); } static void screenproc(void *v) { + plock(&scr); + _screeninit(); + pwakeup(&scr); + punlock(&scr); RunApplicationEventLoop(); + iprint("screenproc exited!\n"); } static OSStatus kbdevent(EventRef); static OSStatus mouseevent(EventRef); static OSStatus +cmdhandler(EventHandlerCallRef next, EventRef event, void *arg) +{ + return eventhandler(next, event, arg); +} + +static OSStatus +quithandler(EventHandlerCallRef next, EventRef event, void *arg) +{ + exit(0); + return 0; +} + +static OSStatus eventhandler(EventHandlerCallRef next, EventRef event, void *arg) { OSStatus result; - + result = CallNextEventHandler(next, event); - + switch(GetEventClass(event)){ case kEventClassKeyboard: return kbdevent(event); - break; case kEventClassMouse: return mouseevent(event); - break; case kEventClassCommand:; HICommand cmd; @@ -241,6 +273,7 @@ mouseevent(EventRef event) GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof op, 0, &op); + osx.xy = subpt(Pt(op.h, op.v), osx.screenr.min); wheel = 0; @@ -279,6 +312,7 @@ mouseevent(EventRef event) break; case kEventMouseMoved: + case kEventMouseDragged: break; default: @@ -432,6 +466,7 @@ eresized(int new) provider, 0, 0, kCGRenderingIntentDefault); CGDataProviderRelease(provider); // CGImageCreate did incref + mouserect = m->r; termreplacescreenimage(m); drawreplacescreenimage(m); // frees old osx.screenimage if any if(osx.image) @@ -455,6 +490,7 @@ flushmemscreen(Rectangle r) cgr.size.width = Dx(r); cgr.size.height = Dy(r); subimg = CGImageCreateWithImageInRect(osx.image, cgr); + cgr.origin.y = Dy(osx.screenr) - r.max.y; // XXX how does this make any sense? CGContextDrawImage(context, cgr, subimg); CGContextFlush(context); CGImageRelease(subimg); diff --git a/src/9vx/osx/signal.c b/src/9vx/osx/signal.c @@ -0,0 +1,237 @@ +/* + * Truly awful code to simulate Unix signal handler dispatch + * using Mach signal handler dispatch. The BSD support routines + * can't deal with our SIGSEGVs properly. Among other things, + * they keep waking up other threads and they cause a popup + * about the application quitting when it hasn't. + * + * This code is inspired by similar code in SBCL. + * See also http://paste.lisp.org/display/19593. + * See also http://lists.apple.com/archives/darwin-dev/2006/Oct/msg00122.html + */ + +#define __DARWIN_UNIX_03 0 + +#include <mach/mach.h> +#include <sys/ucontext.h> +#include <pthread.h> +#include <signal.h> +#include <sys/signal.h> + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern void sigsegv(int, siginfo_t*, void*); /* provided by Plan 9 VX */ +extern int invx32; + +static x86_thread_state32_t normal; /* normal segment registers */ +static void *altstack; + +/* + * Manipulate stack in regs. + */ +static void +push(x86_thread_state32_t *regs, ulong data) +{ + uint *sp; + + sp = (uint*)regs->esp; + *--sp = data; + regs->esp = (uint)sp; +} + +static void +align(x86_thread_state32_t *regs) +{ + uint *sp; + + sp = (uint*)regs->esp; + while((ulong)sp & 15) + sp--; + regs->esp = (uint)sp; +} + +static void* +alloc(x86_thread_state32_t *regs, int n) +{ + n = (n+15) & ~15; + regs->esp -= n; + return (void*)regs->esp; +} + +/* + * Signal handler wrapper. Calls handler and then + * causes an illegal instruction exception to jump + * back to us. + */ +static void +wrapper(siginfo_t *siginfo, + mcontext_t mcontext, + void (*handler)(int, siginfo_t*, void*)) +{ + ucontext_t ucontext; + + memset(&ucontext, 0, sizeof ucontext); + ucontext.uc_mcontext = mcontext; + handler(siginfo->si_signo, siginfo, &ucontext); + + /* Cause EXC_BAD_INSTRUCTION to "exit" signal handler */ + asm volatile( + "movl %0, %%eax\n" + "movl $0xdeadbeef, %%esp\n" + ".long 0xffff0b0f\n" + : : "r" (mcontext)); +} + +void +dumpmcontext(mcontext_t m) +{ + x86_thread_state32_t *ureg; + + ureg = &m->ss; + iprint("FLAGS=%luX TRAP=%luX ECODE=%luX PC=%luX CR2=%luX\n", + ureg->eflags, m->es.trapno, m->es.err, ureg->eip, m->es.faultvaddr); + iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", + ureg->eax, ureg->ebx, ureg->ecx, ureg->edx); + iprint(" SI %8.8luX DI %8.8luX BP %8.8luX SP %8.8luX\n", + ureg->esi, ureg->edi, ureg->ebp, ureg->esp); +} + +void +dumpregs1(x86_thread_state32_t *ureg) +{ + iprint("FLAGS=%luX PC=%luX\n", + ureg->eflags, ureg->eip); + iprint(" AX %8.8luX BX %8.8luX CX %8.8luX DX %8.8luX\n", + ureg->eax, ureg->ebx, ureg->ecx, ureg->edx); + iprint(" SI %8.8luX DI %8.8luX BP %8.8luX SP %8.8luX\n", + ureg->esi, ureg->edi, ureg->ebp, ureg->esp); +} + + +/* + * Called by mach loop in exception handling thread. + */ +kern_return_t +catch_exception_raise(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code_vector, + mach_msg_type_number_t code_count) +{ + mach_msg_type_number_t n; + x86_thread_state32_t regs, save_regs; + siginfo_t *stk_siginfo; + kern_return_t ret; + uint *sp; + mcontext_t stk_mcontext; + + n = x86_THREAD_STATE32_COUNT; + ret = thread_get_state(thread, x86_THREAD_STATE32, (void*)&regs, &n); + if(ret != KERN_SUCCESS) + panic("mach get regs failed: %d", ret); + + switch(exception){ + case EXC_BAD_ACCESS: + save_regs = regs; + if(invx32) + regs.esp = (uint)altstack; + else if(regs.ss != normal.ss) + panic("not in vx32 but bogus ss"); + align(&regs); + regs.cs = normal.cs; + regs.ds = normal.ds; + regs.es = normal.es; + regs.ss = normal.ss; + + stk_siginfo = alloc(&regs, sizeof *stk_siginfo); + stk_mcontext = alloc(&regs, sizeof *stk_mcontext); + + memset(stk_siginfo, 0, sizeof *stk_siginfo); + stk_siginfo->si_signo = SIGBUS; + stk_siginfo->si_addr = (void*)code_vector[1]; + + stk_mcontext->ss = save_regs; + n = x86_FLOAT_STATE32_COUNT; + ret = thread_get_state(thread, x86_FLOAT_STATE32, (void*)&stk_mcontext->fs, &n); + if(ret != KERN_SUCCESS) + panic("mach get fpregs failed: %d", ret); + n = x86_EXCEPTION_STATE32_COUNT; + ret = thread_get_state(thread, x86_EXCEPTION_STATE32, (void*)&stk_mcontext->es, &n); + if(ret != KERN_SUCCESS) + panic("mach get eregs: %d", ret); + + sp = alloc(&regs, 3*4); + sp[0] = (uint)stk_siginfo; + sp[1] = (uint)stk_mcontext; + sp[2] = (uint)sigsegv; + + push(&regs, regs.eip); /* for debugger; wrapper won't return */ + regs.eip = (uint)wrapper; + + ret = thread_set_state(thread, x86_THREAD_STATE32, + (void*)&regs, x86_THREAD_STATE32_COUNT); + if(ret != KERN_SUCCESS) + panic("mach set regs failed: %d", ret); + return KERN_SUCCESS; + + case EXC_BAD_INSTRUCTION: + /* Thread signalling that it's done with sigsegv. */ + if(regs.esp != 0xdeadbeef){ + dumpregs1(&regs); + return KERN_INVALID_RIGHT; + panic("bad instruction eip=%p", regs.eip); + } + stk_mcontext = (mcontext_t)regs.eax; + ret = thread_set_state(thread, x86_THREAD_STATE32, + (void*)&stk_mcontext->ss, x86_THREAD_STATE32_COUNT); + if(ret != KERN_SUCCESS) + panic("mach set regs1 failed: %d", ret); + ret = thread_set_state(thread, x86_FLOAT_STATE32, + (void*)&stk_mcontext->fs, x86_FLOAT_STATE32_COUNT); + if(ret != KERN_SUCCESS) + panic("mach set fpregs failed: %d", ret); + return KERN_SUCCESS; + } + return KERN_INVALID_RIGHT; +} + +static void* +handler(void *v) +{ + extern boolean_t exc_server(); + mach_port_t port; + + setmach(machp[0]); + port = (mach_port_t)v; + mach_msg_server(exc_server, 2048, port, 0); + return 0; /* not reached */ +} + +void +machsiginit(void) +{ + mach_port_t port; + pthread_t pid; + stack_t ss; + int ret; + + extern int vx32_getcontext(x86_thread_state32_t*); + vx32_getcontext(&normal); + + if(sigaltstack(nil, &ss) < 0 || (ss.ss_flags & SS_DISABLE)) + panic("machsiginit: no alt stack"); + altstack = ss.ss_sp + ss.ss_size; + + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); + mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); + ret = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION, port, + EXCEPTION_DEFAULT, MACHINE_THREAD_STATE); + pthread_create(&pid, nil, handler, (void*)port); +} + diff --git a/src/9vx/sched.c b/src/9vx/sched.c @@ -21,27 +21,24 @@ #include "error.h" #include "trace.h" -/* Pthreads-based sleep and wakeup. */ -typedef struct Psleep Psleep; -struct Psleep +void +pinit(Psleep *p) { - pthread_mutex_t mutex; - pthread_cond_t cond; -}; +} -static void +void plock(Psleep *p) { pthread_mutex_lock(&p->mutex); } -static void +void punlock(Psleep *p) { pthread_mutex_unlock(&p->mutex); } -static void +void psleep(Psleep *p) { /* @@ -58,7 +55,7 @@ psleep(Psleep *p) pthread_cond_wait(&p->cond, &p->mutex); } -static void +void pwakeup(Psleep *p) { pthread_cond_signal(&p->cond); diff --git a/src/9vx/stub.c b/src/9vx/stub.c @@ -544,10 +544,7 @@ panic(char *fmt, ...) if(doabort){ for(;;) microdelay(1000000); - signal(SIGSEGV, SIG_IGN); - *(int*)0 = 0; } - // abort(); exit(0); } diff --git a/src/9vx/term.c b/src/9vx/term.c @@ -67,6 +67,9 @@ terminit(int printing) /* x11 will call termreplacescreenimage when it is ready */ unlock(&term.lk); + /* maybe it already has */ + if(term.screen) + termreplacescreenimage(term.screen); /* If we're the output mechanism, set it up and kick off the screen. */ if(printing) @@ -106,6 +109,12 @@ _termreplacescreenimage(Memimage *m) int h; Rectangle r, r0; + if(term.bg == nil){ + /* not yet init */ + term.screen = m; + return; + } + /* white background */ if(!mouse.open) memimagedraw(m, m->r, term.bg, ZP, memopaque, ZP, S); diff --git a/src/9vx/x11/x11-kernel.c b/src/9vx/x11/x11-kernel.c @@ -110,6 +110,11 @@ _xproc(void *v) } } +void +screeninit(void) +{ +} + uchar* attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen, void **X) @@ -175,3 +180,4 @@ setmouse(Point p) _xmoveto(p); drawqunlock(); } +