debuggers.hg

view tools/libxc/xc_ptrace.c @ 21067:b4a1832a916f

Update Xen version to 4.0.0-rc6
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 09 18:18:05 2010 +0000 (2010-03-09)
parents 2f9e1348aa98
children 3ffdb094c2c0 0a91254533dc
line source
1 #include <sys/ptrace.h>
2 #include <sys/wait.h>
3 #include <time.h>
5 #include "xc_private.h"
6 #include "xg_private.h"
7 #include "xc_ptrace.h"
9 #ifdef DEBUG
10 static char *ptrace_names[] = {
11 "PTRACE_TRACEME",
12 "PTRACE_PEEKTEXT",
13 "PTRACE_PEEKDATA",
14 "PTRACE_PEEKUSER",
15 "PTRACE_POKETEXT",
16 "PTRACE_POKEDATA",
17 "PTRACE_POKEUSER",
18 "PTRACE_CONT",
19 "PTRACE_KILL",
20 "PTRACE_SINGLESTEP",
21 "PTRACE_INVALID",
22 "PTRACE_INVALID",
23 "PTRACE_GETREGS",
24 "PTRACE_SETREGS",
25 "PTRACE_GETFPREGS",
26 "PTRACE_SETFPREGS",
27 "PTRACE_ATTACH",
28 "PTRACE_DETACH",
29 "PTRACE_GETFPXREGS",
30 "PTRACE_SETFPXREGS",
31 "PTRACE_INVALID",
32 "PTRACE_INVALID",
33 "PTRACE_INVALID",
34 "PTRACE_INVALID",
35 "PTRACE_SYSCALL",
36 };
37 #endif
39 static int current_domid = -1;
40 static int current_isfile;
41 static int current_is_hvm;
43 static uint64_t online_cpumap;
44 static uint64_t regs_valid;
45 static unsigned int nr_vcpu_ids;
46 static vcpu_guest_context_any_t *ctxt;
48 #define FOREACH_CPU(cpumap, i) for ( cpumap = online_cpumap; (i = xc_ffs64(cpumap)); cpumap &= ~(1 << (index - 1)) )
50 static int
51 fetch_regs(int xc_handle, int cpu, int *online)
52 {
53 xc_vcpuinfo_t info;
54 int retval = 0;
56 if (online)
57 *online = 0;
58 if ( !(regs_valid & (1 << cpu)) )
59 {
60 retval = xc_vcpu_getcontext(xc_handle, current_domid,
61 cpu, &ctxt[cpu]);
62 if ( retval )
63 goto done;
64 regs_valid |= (1 << cpu);
66 }
67 if ( online == NULL )
68 goto done;
70 retval = xc_vcpu_getinfo(xc_handle, current_domid, cpu, &info);
71 *online = info.online;
73 done:
74 return retval;
75 }
77 static struct thr_ev_handlers {
78 thr_ev_handler_t td_create;
79 thr_ev_handler_t td_death;
80 } handlers;
82 void
83 xc_register_event_handler(thr_ev_handler_t h,
84 td_event_e e)
85 {
86 switch (e) {
87 case TD_CREATE:
88 handlers.td_create = h;
89 break;
90 case TD_DEATH:
91 handlers.td_death = h;
92 break;
93 default:
94 abort(); /* XXX */
95 }
96 }
98 static inline int
99 paging_enabled(vcpu_guest_context_any_t *v)
100 {
101 unsigned long cr0 = v->c.ctrlreg[0];
102 return (cr0 & X86_CR0_PE) && (cr0 & X86_CR0_PG);
103 }
105 vcpu_guest_context_any_t *xc_ptrace_get_vcpu_ctxt(unsigned int nr_cpus)
106 {
107 if (nr_cpus > nr_vcpu_ids) {
108 vcpu_guest_context_any_t *new;
110 new = realloc(ctxt, nr_cpus * sizeof(*ctxt));
111 if (!new)
112 return NULL;
113 ctxt = new;
114 nr_vcpu_ids = nr_cpus;
115 }
117 return ctxt;
118 }
120 /*
121 * Fetch registers for all online cpus and set the cpumap
122 * to indicate which cpus are online
123 *
124 */
126 static int
127 get_online_cpumap(int xc_handle, struct xen_domctl_getdomaininfo *d,
128 uint64_t *cpumap)
129 {
130 int i, online;
132 if (!xc_ptrace_get_vcpu_ctxt(d->max_vcpu_id + 1))
133 return -ENOMEM;
135 *cpumap = 0;
136 for (i = 0; i <= d->max_vcpu_id; i++) {
137 fetch_regs(xc_handle, i, &online);
138 if (online)
139 *cpumap |= (1 << i);
140 }
142 return (*cpumap == 0) ? -1 : 0;
143 }
145 /*
146 * Notify GDB of any vcpus that have come online or gone offline
147 * update online_cpumap
148 *
149 */
151 static void
152 online_vcpus_changed(uint64_t cpumap)
153 {
154 uint64_t changed_cpumap = cpumap ^ online_cpumap;
155 int index;
157 while ( (index = xc_ffs64(changed_cpumap)) ) {
158 if ( cpumap & (1 << (index - 1)) )
159 {
160 if (handlers.td_create) handlers.td_create(index - 1);
161 } else {
162 IPRINTF("thread death: %d\n", index - 1);
163 if (handlers.td_death) handlers.td_death(index - 1);
164 }
165 changed_cpumap &= ~(1 << (index - 1));
166 }
167 online_cpumap = cpumap;
169 }
172 static void *
173 map_domain_va(
174 int xc_handle,
175 int cpu,
176 void *guest_va,
177 int perm)
178 {
179 unsigned long va = (unsigned long)guest_va;
180 unsigned long mfn;
181 void *map;
183 /* cross page boundary */
184 if ( (va & ~PAGE_MASK) + sizeof(long) > PAGE_SIZE )
185 return NULL;
187 mfn = xc_translate_foreign_address(xc_handle, current_domid, cpu, va);
188 if ( mfn == 0 )
189 return NULL;
191 map = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE,
192 perm, mfn);
193 if (map == NULL)
194 return NULL;
196 return map + (va & ~PAGE_MASK);
197 }
199 static void
200 unmap_domain_va(void *guest_va)
201 {
202 munmap((void *)((unsigned long)guest_va & PAGE_MASK), PAGE_SIZE);
203 }
205 int control_c_pressed_flag = 0;
207 static int
208 __xc_waitdomain(
209 int xc_handle,
210 int domain,
211 int *status,
212 int options)
213 {
214 DECLARE_DOMCTL;
215 int retval;
216 struct timespec ts;
217 uint64_t cpumap;
219 ts.tv_sec = 0;
220 ts.tv_nsec = 10*1000*1000;
222 domctl.cmd = XEN_DOMCTL_getdomaininfo;
223 domctl.domain = domain;
225 retry:
226 retval = do_domctl(xc_handle, &domctl);
227 if ( retval || (domctl.domain != domain) )
228 {
229 IPRINTF("getdomaininfo failed\n");
230 goto done;
231 }
232 *status = domctl.u.getdomaininfo.flags;
234 if ( options & WNOHANG )
235 goto done;
237 if (control_c_pressed_flag) {
238 xc_domain_pause(xc_handle, domain);
239 control_c_pressed_flag = 0;
240 goto done;
241 }
243 if ( !(domctl.u.getdomaininfo.flags & XEN_DOMINF_paused) )
244 {
245 nanosleep(&ts,NULL);
246 goto retry;
247 }
248 done:
249 if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap))
250 IPRINTF("get_online_cpumap failed\n");
251 if (online_cpumap != cpumap)
252 online_vcpus_changed(cpumap);
253 return retval;
255 }
258 long
259 xc_ptrace(
260 int xc_handle,
261 enum __ptrace_request request,
262 uint32_t domid_tid,
263 long eaddr,
264 long edata)
265 {
266 DECLARE_DOMCTL;
267 struct gdb_regs pt;
268 long retval = 0;
269 unsigned long *guest_va;
270 uint64_t cpumap;
271 int cpu, index;
272 void *addr = (char *)eaddr;
273 void *data = (char *)edata;
275 cpu = (request != PTRACE_ATTACH) ? domid_tid : 0;
277 switch ( request )
278 {
279 case PTRACE_PEEKTEXT:
280 case PTRACE_PEEKDATA:
281 if (current_isfile)
282 guest_va = (unsigned long *)map_domain_va_core(
283 current_domid, cpu, addr);
284 else
285 guest_va = (unsigned long *)map_domain_va(
286 xc_handle, cpu, addr, PROT_READ);
287 if ( guest_va == NULL )
288 goto out_error;
289 retval = *guest_va;
290 if (!current_isfile)
291 unmap_domain_va(guest_va);
292 break;
294 case PTRACE_POKETEXT:
295 case PTRACE_POKEDATA:
296 /* XXX assume that all CPUs have the same address space */
297 if (current_isfile)
298 guest_va = (unsigned long *)map_domain_va_core(
299 current_domid, cpu, addr);
300 else
301 guest_va = (unsigned long *)map_domain_va(
302 xc_handle, cpu, addr, PROT_READ|PROT_WRITE);
303 if ( guest_va == NULL )
304 goto out_error;
305 *guest_va = edata;
306 if (!current_isfile)
307 unmap_domain_va(guest_va);
308 break;
310 case PTRACE_GETREGS:
311 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
312 goto out_error;
313 SET_PT_REGS(pt, ctxt[cpu].c.user_regs);
314 memcpy(data, &pt, sizeof(struct gdb_regs));
315 break;
317 case PTRACE_GETFPREGS:
318 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
319 goto out_error;
320 memcpy(data, &ctxt[cpu].c.fpu_ctxt, sizeof (elf_fpregset_t));
321 break;
323 case PTRACE_GETFPXREGS:
324 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
325 goto out_error;
326 memcpy(data, &ctxt[cpu].c.fpu_ctxt, sizeof(ctxt[cpu].c.fpu_ctxt));
327 break;
329 case PTRACE_SETREGS:
330 if (current_isfile)
331 goto out_unsupported; /* XXX not yet supported */
332 SET_XC_REGS(((struct gdb_regs *)data), ctxt[cpu].c.user_regs);
333 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
334 &ctxt[cpu])))
335 goto out_error_domctl;
336 break;
338 case PTRACE_SINGLESTEP:
339 if (current_isfile)
340 goto out_unsupported; /* XXX not yet supported */
341 /* XXX we can still have problems if the user switches threads
342 * during single-stepping - but that just seems retarded
343 */
344 /* Try to enalbe Monitor Trap Flag for HVM, and fall back to TF
345 * if no MTF support
346 */
347 if ( !current_is_hvm ||
348 xc_domain_debug_control(xc_handle,
349 current_domid,
350 XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON,
351 cpu) )
352 {
353 ctxt[cpu].c.user_regs.eflags |= PSL_T;
354 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
355 &ctxt[cpu])))
356 goto out_error_domctl;
357 }
358 /* FALLTHROUGH */
360 case PTRACE_CONT:
361 case PTRACE_DETACH:
362 if (current_isfile)
363 goto out_unsupported; /* XXX not yet supported */
364 if ( request != PTRACE_SINGLESTEP )
365 {
366 FOREACH_CPU(cpumap, index) {
367 cpu = index - 1;
368 if ( !current_is_hvm ||
369 xc_domain_debug_control(xc_handle,
370 current_domid,
371 XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF,
372 cpu) )
373 {
374 if (fetch_regs(xc_handle, cpu, NULL))
375 goto out_error;
376 /* Clear trace flag */
377 if ( ctxt[cpu].c.user_regs.eflags & PSL_T )
378 {
379 ctxt[cpu].c.user_regs.eflags &= ~PSL_T;
380 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid,
381 cpu, &ctxt[cpu])))
382 goto out_error_domctl;
383 }
384 }
385 }
386 }
387 if ( request == PTRACE_DETACH )
388 {
389 if ((retval = xc_domain_setdebugging(xc_handle, current_domid, 0)))
390 goto out_error_domctl;
391 }
392 regs_valid = 0;
393 if ((retval = xc_domain_unpause(xc_handle, current_domid > 0 ?
394 current_domid : -current_domid)))
395 goto out_error_domctl;
396 break;
398 case PTRACE_ATTACH:
399 current_domid = domid_tid;
400 current_isfile = (int)edata;
401 if (current_isfile)
402 break;
403 domctl.cmd = XEN_DOMCTL_getdomaininfo;
404 domctl.domain = current_domid;
405 retval = do_domctl(xc_handle, &domctl);
406 if ( retval || (domctl.domain != current_domid) )
407 goto out_error_domctl;
408 if ( domctl.u.getdomaininfo.flags & XEN_DOMINF_paused )
409 IPRINTF("domain currently paused\n");
410 else if ((retval = xc_domain_pause(xc_handle, current_domid)))
411 goto out_error_domctl;
412 current_is_hvm = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_hvm_guest);
413 if ((retval = xc_domain_setdebugging(xc_handle, current_domid, 1)))
414 goto out_error_domctl;
416 if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap))
417 IPRINTF("get_online_cpumap failed\n");
418 if (online_cpumap != cpumap)
419 online_vcpus_changed(cpumap);
420 break;
422 case PTRACE_TRACEME:
423 IPRINTF("PTRACE_TRACEME is an invalid request under Xen\n");
424 goto out_error;
426 default:
427 goto out_unsupported; /* XXX not yet supported */
428 }
430 return retval;
432 out_error_domctl:
433 perror("domctl failed");
434 out_error:
435 errno = EINVAL;
436 return retval;
438 out_unsupported:
439 #ifdef DEBUG
440 IPRINTF("unsupported xc_ptrace request %s\n", ptrace_names[request]);
441 #endif
442 errno = ENOSYS;
443 return -1;
445 }
447 int
448 xc_waitdomain(
449 int xc_handle,
450 int domain,
451 int *status,
452 int options)
453 {
454 if (current_isfile)
455 return xc_waitdomain_core(xc_handle, domain, status, options);
456 return __xc_waitdomain(xc_handle, domain, status, options);
457 }
459 /*
460 * Local variables:
461 * mode: C
462 * c-set-style: "BSD"
463 * c-basic-offset: 4
464 * tab-width: 4
465 * indent-tabs-mode: nil
466 * End:
467 */