Added server-side infrastructure for the thread input structure.
[wine] / server / context_i386.c
1 /*
2  * i386 register context support
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #ifdef __i386__
24
25 #include <assert.h>
26 #include <errno.h>
27 #ifdef HAVE_SYS_REG_H
28 #include <sys/reg.h>
29 #endif
30 #include <unistd.h>
31 #ifdef HAVE_SYS_PTRACE_H
32 # include <sys/ptrace.h>
33 #endif
34 #ifdef HAVE_SYS_PARAM_H
35 # include <sys/param.h>
36 #endif
37
38 #include "winbase.h"
39 #include "thread.h"
40 #include "request.h"
41
42 #ifndef PTRACE_PEEKUSER
43 #  ifdef PTRACE_PEEKUSR /* libc5 uses USR not USER */
44 #    define PTRACE_PEEKUSER PTRACE_PEEKUSR
45 #  else
46 #    define PTRACE_PEEKUSER PT_READ_U
47 #  endif
48 #endif
49
50 #ifndef PTRACE_POKEUSER
51 #  ifdef PTRACE_POKEUSR /* libc5 uses USR not USER */
52 #    define PTRACE_POKEUSER PTRACE_POKEUSR
53 #  else
54 #    define PTRACE_POKEUSER PT_WRITE_U
55 #  endif
56 #endif
57
58 #ifndef PTRACE_GETREGS
59 #define PTRACE_GETREGS PT_GETREGS
60 #endif
61 #ifndef PTRACE_GETFPREGS
62 #define PTRACE_GETFPREGS PT_GETFPREGS
63 #endif
64 #ifndef PTRACE_SETREGS
65 #define PTRACE_SETREGS PT_SETREGS
66 #endif
67 #ifndef PTRACE_SETFPREGS
68 #define PTRACE_SETFPREGS PT_SETFPREGS
69 #endif
70
71 #ifdef PT_GETDBREGS
72 #define PTRACE_GETDBREGS PT_GETDBREGS
73 #endif
74
75 #ifdef PT_SETDBREGS
76 #define PTRACE_SETDBREGS PT_SETDBREGS
77 #endif
78
79 #ifdef linux
80 #ifdef HAVE_SYS_USER_H
81 # include <sys/user.h>
82 #endif
83
84 /* user_regs definitions from asm/user.h */
85 struct kernel_user_regs_struct
86 {
87     long ebx, ecx, edx, esi, edi, ebp, eax;
88     unsigned short ds, __ds, es, __es;
89     unsigned short fs, __fs, gs, __gs;
90     long orig_eax, eip;
91     unsigned short cs, __cs;
92     long eflags, esp;
93     unsigned short ss, __ss;
94 };
95
96 /* debug register offset in struct user */
97 #define DR_OFFSET(dr) ((int)((((struct user *)0)->u_debugreg) + (dr)))
98
99 /* retrieve a debug register */
100 static inline int get_debug_reg( int pid, int num, DWORD *data )
101 {
102     int res = ptrace( PTRACE_PEEKUSER, pid, DR_OFFSET(num), 0 );
103     if ((res == -1) && errno)
104     {
105         file_set_error();
106         return -1;
107     }
108     *data = res;
109     return 0;
110 }
111
112 /* retrieve a thread context */
113 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
114 {
115     int pid = thread->unix_pid;
116     if (flags & CONTEXT_FULL)
117     {
118         struct kernel_user_regs_struct regs;
119         if (ptrace( PTRACE_GETREGS, pid, 0, &regs ) == -1) goto error;
120         if (flags & CONTEXT_INTEGER)
121         {
122             context->Eax = regs.eax;
123             context->Ebx = regs.ebx;
124             context->Ecx = regs.ecx;
125             context->Edx = regs.edx;
126             context->Esi = regs.esi;
127             context->Edi = regs.edi;
128         }
129         if (flags & CONTEXT_CONTROL)
130         {
131             context->Ebp    = regs.ebp;
132             context->Esp    = regs.esp;
133             context->Eip    = regs.eip;
134             context->SegCs  = regs.cs;
135             context->SegSs  = regs.ss;
136             context->EFlags = regs.eflags;
137         }
138         if (flags & CONTEXT_SEGMENTS)
139         {
140             context->SegDs = regs.ds;
141             context->SegEs = regs.es;
142             context->SegFs = regs.fs;
143             context->SegGs = regs.gs;
144         }
145     }
146     if (flags & CONTEXT_DEBUG_REGISTERS)
147     {
148         if (get_debug_reg( pid, 0, &context->Dr0 ) == -1) goto error;
149         if (get_debug_reg( pid, 1, &context->Dr1 ) == -1) goto error;
150         if (get_debug_reg( pid, 2, &context->Dr2 ) == -1) goto error;
151         if (get_debug_reg( pid, 3, &context->Dr3 ) == -1) goto error;
152         if (get_debug_reg( pid, 6, &context->Dr6 ) == -1) goto error;
153         if (get_debug_reg( pid, 7, &context->Dr7 ) == -1) goto error;
154     }
155     if (flags & CONTEXT_FLOATING_POINT)
156     {
157         /* we can use context->FloatSave directly as it is using the */
158         /* correct structure (the same as fsave/frstor) */
159         if (ptrace( PTRACE_GETFPREGS, pid, 0, &context->FloatSave ) == -1) goto error;
160         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
161     }
162     return;
163  error:
164     file_set_error();
165 }
166
167
168 /* set a thread context */
169 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
170 {
171     int pid = thread->unix_pid;
172     if (flags & CONTEXT_FULL)
173     {
174         struct kernel_user_regs_struct regs;
175
176         /* need to preserve some registers (at a minimum orig_eax must always be preserved) */
177         if (ptrace( PTRACE_GETREGS, pid, 0, &regs ) == -1) goto error;
178
179         if (flags & CONTEXT_INTEGER)
180         {
181             regs.eax = context->Eax;
182             regs.ebx = context->Ebx;
183             regs.ecx = context->Ecx;
184             regs.edx = context->Edx;
185             regs.esi = context->Esi;
186             regs.edi = context->Edi;
187         }
188         if (flags & CONTEXT_CONTROL)
189         {
190             regs.ebp = context->Ebp;
191             regs.esp = context->Esp;
192             regs.eip = context->Eip;
193             regs.cs  = context->SegCs;
194             regs.ss  = context->SegSs;
195             regs.eflags = context->EFlags;
196         }
197         if (flags & CONTEXT_SEGMENTS)
198         {
199             regs.ds = context->SegDs;
200             regs.es = context->SegEs;
201             regs.fs = context->SegFs;
202             regs.gs = context->SegGs;
203         }
204         if (ptrace( PTRACE_SETREGS, pid, 0, &regs ) == -1) goto error;
205     }
206     if (flags & CONTEXT_DEBUG_REGISTERS)
207     {
208         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->Dr0 ) == -1) goto error;
209         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->Dr1 ) == -1) goto error;
210         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->Dr2 ) == -1) goto error;
211         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->Dr3 ) == -1) goto error;
212         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->Dr6 ) == -1) goto error;
213         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->Dr7 ) == -1) goto error;
214     }
215     if (flags & CONTEXT_FLOATING_POINT)
216     {
217         /* we can use context->FloatSave directly as it is using the */
218         /* correct structure (the same as fsave/frstor) */
219         if (ptrace( PTRACE_SETFPREGS, pid, 0, &context->FloatSave ) == -1) goto error;
220     }
221     return;
222  error:
223     file_set_error();
224 }
225
226 #elif defined(__sun) || defined(__sun__)
227
228 /* retrieve a thread context */
229 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
230 {
231     int pid = thread->unix_pid;
232     if (flags & CONTEXT_FULL)
233     {
234         struct regs regs;
235         if (ptrace( PTRACE_GETREGS, pid, (int) &regs, 0 ) == -1) goto error;
236         if (flags & CONTEXT_INTEGER)
237         {
238             context->Eax = regs.r_eax;
239             context->Ebx = regs.r_ebx;
240             context->Ecx = regs.r_ecx;
241             context->Edx = regs.r_edx;
242             context->Esi = regs.r_esi;
243             context->Edi = regs.r_edi;
244         }
245         if (flags & CONTEXT_CONTROL)
246         {
247             context->Ebp    = regs.r_ebp;
248             context->Esp    = regs.r_esp;
249             context->Eip    = regs.r_eip;
250             context->SegCs  = regs.r_cs & 0xffff;
251             context->SegSs  = regs.r_ss & 0xffff;
252             context->EFlags = regs.r_efl;
253         }
254         if (flags & CONTEXT_SEGMENTS)
255         {
256             context->SegDs = regs.r_ds & 0xffff;
257             context->SegEs = regs.r_es & 0xffff;
258             context->SegFs = regs.r_fs & 0xffff;
259             context->SegGs = regs.r_gs & 0xffff;
260         }
261     }
262     if (flags & CONTEXT_DEBUG_REGISTERS)
263     {
264         /* FIXME: How is this done on Solaris? */
265     }
266     if (flags & CONTEXT_FLOATING_POINT)
267     {
268         /* we can use context->FloatSave directly as it is using the */
269         /* correct structure (the same as fsave/frstor) */
270         if (ptrace( PTRACE_GETFPREGS, pid, (int) &context->FloatSave, 0 ) == -1) goto error;
271         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
272     }
273     return;
274  error:
275     file_set_error();
276 }
277
278
279 /* set a thread context */
280 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
281 {
282     int pid = thread->unix_pid;
283     if (flags & CONTEXT_FULL)
284     {
285         struct regs regs;
286         if (((flags | CONTEXT_i386) & CONTEXT_FULL) != CONTEXT_FULL)
287         {
288             /* need to preserve some registers */
289             if (ptrace( PTRACE_GETREGS, pid, (int) &regs, 0 ) == -1) goto error;
290         }
291         if (flags & CONTEXT_INTEGER)
292         {
293             regs.r_eax = context->Eax;
294             regs.r_ebx = context->Ebx;
295             regs.r_ecx = context->Ecx;
296             regs.r_edx = context->Edx;
297             regs.r_esi = context->Esi;
298             regs.r_edi = context->Edi;
299         }
300         if (flags & CONTEXT_CONTROL)
301         {
302             regs.r_ebp = context->Ebp;
303             regs.r_esp = context->Esp;
304             regs.r_eip = context->Eip;
305             regs.r_cs = context->SegCs;
306             regs.r_ss = context->SegSs;
307             regs.r_efl = context->EFlags;
308         }
309         if (flags & CONTEXT_SEGMENTS)
310         {
311             regs.r_ds = context->SegDs;
312             regs.r_es = context->SegEs;
313             regs.r_fs = context->SegFs;
314             regs.r_gs = context->SegGs;
315         }
316         if (ptrace( PTRACE_SETREGS, pid, (int) &regs, 0 ) == -1) goto error;
317     }
318     if (flags & CONTEXT_DEBUG_REGISTERS)
319     {
320         /* FIXME: How is this done on Solaris? */
321     }
322     if (flags & CONTEXT_FLOATING_POINT)
323     {
324         /* we can use context->FloatSave directly as it is using the */
325         /* correct structure (the same as fsave/frstor) */
326         if (ptrace( PTRACE_SETFPREGS, pid, (int) &context->FloatSave, 0 ) == -1) goto error;
327     }
328     return;
329  error:
330     file_set_error();
331 }
332
333 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
334 #include <machine/reg.h>
335
336 /* retrieve a thread context */
337 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
338 {
339     int pid = thread->unix_pid;
340     if (flags & CONTEXT_FULL)
341     {
342         struct reg regs;
343         if (ptrace( PTRACE_GETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
344         if (flags & CONTEXT_INTEGER)
345         {
346             context->Eax = regs.r_eax;
347             context->Ebx = regs.r_ebx;
348             context->Ecx = regs.r_ecx;
349             context->Edx = regs.r_edx;
350             context->Esi = regs.r_esi;
351             context->Edi = regs.r_edi;
352         }
353         if (flags & CONTEXT_CONTROL)
354         {
355             context->Ebp    = regs.r_ebp;
356             context->Esp    = regs.r_esp;
357             context->Eip    = regs.r_eip;
358             context->SegCs  = regs.r_cs & 0xffff;
359             context->SegSs  = regs.r_ss & 0xffff;
360             context->EFlags = regs.r_eflags;
361         }
362         if (flags & CONTEXT_SEGMENTS)
363         {
364             context->SegDs = regs.r_ds & 0xffff;
365             context->SegEs = regs.r_es & 0xffff;
366             context->SegFs = regs.r_fs & 0xffff;
367             context->SegGs = regs.r_gs & 0xffff;
368         }
369     }
370     if (flags & CONTEXT_DEBUG_REGISTERS)
371     {
372 #ifdef PTRACE_GETDBREGS
373         struct dbreg dbregs;
374         if (ptrace( PTRACE_GETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1)
375                 goto error;
376         context->Dr0 = dbregs.dr0;
377         context->Dr1 = dbregs.dr1;
378         context->Dr2 = dbregs.dr2;
379         context->Dr3 = dbregs.dr3;
380         context->Dr6 = dbregs.dr6;
381         context->Dr7 = dbregs.dr7;
382 #endif
383     }
384     if (flags & CONTEXT_FLOATING_POINT)
385     {
386         /* we can use context->FloatSave directly as it is using the */
387         /* correct structure (the same as fsave/frstor) */
388         if (ptrace( PTRACE_GETFPREGS, pid, (caddr_t) &context->FloatSave, 0 ) == -1) goto error;
389         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
390     }
391     return;
392  error:
393     file_set_error();
394 }
395
396
397 /* set a thread context */
398 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
399 {
400     int pid = thread->unix_pid;
401     if (flags & CONTEXT_FULL)
402     {
403         struct reg regs;
404         if (((flags | CONTEXT_i386) & CONTEXT_FULL) != CONTEXT_FULL)
405         {
406             /* need to preserve some registers */
407             if (ptrace( PTRACE_GETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
408         }
409         if (flags & CONTEXT_INTEGER)
410         {
411             regs.r_eax = context->Eax;
412             regs.r_ebx = context->Ebx;
413             regs.r_ecx = context->Ecx;
414             regs.r_edx = context->Edx;
415             regs.r_esi = context->Esi;
416             regs.r_edi = context->Edi;
417         }
418         if (flags & CONTEXT_CONTROL)
419         {
420             regs.r_ebp = context->Ebp;
421             regs.r_esp = context->Esp;
422             regs.r_eip = context->Eip;
423             regs.r_cs = context->SegCs;
424             regs.r_ss = context->SegSs;
425             regs.r_eflags = context->EFlags;
426         }
427         if (flags & CONTEXT_SEGMENTS)
428         {
429             regs.r_ds = context->SegDs;
430             regs.r_es = context->SegEs;
431             regs.r_fs = context->SegFs;
432             regs.r_gs = context->SegGs;
433         }
434         if (ptrace( PTRACE_SETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
435     }
436     if (flags & CONTEXT_DEBUG_REGISTERS)
437     {
438 #ifdef PTRACE_SETDBREGS
439         struct dbreg dbregs;
440         dbregs.dr0 = context->Dr0;
441         dbregs.dr1 = context->Dr1;
442         dbregs.dr2 = context->Dr2;
443         dbregs.dr3 = context->Dr3;
444         dbregs.dr4 = 0;
445         dbregs.dr5 = 0;
446         dbregs.dr6 = context->Dr6;
447         dbregs.dr7 = context->Dr7;
448         if (ptrace( PTRACE_SETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1)
449                 goto error;
450 #endif
451     }
452     if (flags & CONTEXT_FLOATING_POINT)
453     {
454         /* we can use context->FloatSave directly as it is using the */
455         /* correct structure (the same as fsave/frstor) */
456         if (ptrace( PTRACE_SETFPREGS, pid, (caddr_t) &context->FloatSave, 0 ) == -1) goto error;
457     }
458     return;
459  error:
460     file_set_error();
461 }
462
463 #else  /* linux || __sun__ || __FreeBSD__ */
464 #error You must implement get/set_thread_context for your platform
465 #endif  /* linux || __sun__ || __FreeBSD__ */
466
467
468 /* copy a context structure according to the flags */
469 static void copy_context( CONTEXT *to, const CONTEXT *from, int flags )
470 {
471     if (flags & CONTEXT_CONTROL)
472     {
473         to->Ebp    = from->Ebp;
474         to->Eip    = from->Eip;
475         to->Esp    = from->Esp;
476         to->SegCs  = from->SegCs;
477         to->SegSs  = from->SegSs;
478         to->EFlags = from->EFlags;
479     }
480     if (flags & CONTEXT_INTEGER)
481     {
482         to->Eax = from->Eax;
483         to->Ebx = from->Ebx;
484         to->Ecx = from->Ecx;
485         to->Edx = from->Edx;
486         to->Esi = from->Esi;
487         to->Edi = from->Edi;
488     }
489     if (flags & CONTEXT_SEGMENTS)
490     {
491         to->SegDs = from->SegDs;
492         to->SegEs = from->SegEs;
493         to->SegFs = from->SegFs;
494         to->SegGs = from->SegGs;
495     }
496     if (flags & CONTEXT_FLOATING_POINT)
497     {
498         to->FloatSave = from->FloatSave;
499     }
500     /* we don't bother copying the debug registers, since they */
501     /* always need to be accessed by ptrace anyway */
502 }
503
504 /* retrieve the current instruction pointer of a thread */
505 void *get_thread_ip( struct thread *thread )
506 {
507     CONTEXT context;
508     context.Eip = 0;
509     if (suspend_for_ptrace( thread ))
510     {
511         get_thread_context( thread, CONTEXT_CONTROL, &context );
512         resume_thread( thread );
513     }
514     return (void *)context.Eip;
515 }
516
517 /* determine if we should continue the thread in single-step mode */
518 int get_thread_single_step( struct thread *thread )
519 {
520     CONTEXT context;
521     if (thread->context) return 0;  /* don't single-step inside exception event */
522     get_thread_context( thread, CONTEXT_CONTROL, &context );
523     return (context.EFlags & 0x100) != 0;
524 }
525
526 /* retrieve the current context of a thread */
527 DECL_HANDLER(get_thread_context)
528 {
529     struct thread *thread;
530     void *data;
531     int flags = req->flags & ~CONTEXT_i386;  /* get rid of CPU id */
532
533     if (get_reply_max_size() < sizeof(CONTEXT))
534     {
535         set_error( STATUS_INVALID_PARAMETER );
536         return;
537     }
538     if (!(thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT ))) return;
539
540     if ((data = set_reply_data_size( sizeof(CONTEXT) )))
541     {
542         /* copy incoming context into reply */
543         memset( data, 0, sizeof(CONTEXT) );
544         memcpy( data, get_req_data(), min( get_req_data_size(), sizeof(CONTEXT) ));
545
546         if (thread->context)  /* thread is inside an exception event */
547         {
548             copy_context( data, thread->context, flags );
549             flags &= CONTEXT_DEBUG_REGISTERS;
550         }
551         if (flags && suspend_for_ptrace( thread ))
552         {
553             get_thread_context( thread, flags, data );
554             resume_thread( thread );
555         }
556     }
557     release_object( thread );
558 }
559
560
561 /* set the current context of a thread */
562 DECL_HANDLER(set_thread_context)
563 {
564     struct thread *thread;
565     int flags = req->flags & ~CONTEXT_i386;  /* get rid of CPU id */
566
567     if (get_req_data_size() < sizeof(CONTEXT))
568     {
569         set_error( STATUS_INVALID_PARAMETER );
570         return;
571     }
572     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
573     {
574         if (thread->context)  /* thread is inside an exception event */
575         {
576             copy_context( thread->context, get_req_data(), flags );
577             flags &= CONTEXT_DEBUG_REGISTERS;
578         }
579         if (flags && suspend_for_ptrace( thread ))
580         {
581             set_thread_context( thread, flags, get_req_data() );
582             resume_thread( thread );
583         }
584         release_object( thread );
585     }
586 }
587
588 #endif  /* __i386__ */