ia64/xen-unstable

view tools/libxc/xc_ptrace.c @ 13608:30af6cfdb05c

Make domctl/sysctl interfaces 32-/64-bit invariant.
This kills off a fair amount of unpleasant CONFIG_COMPAT shimming and
avoids needing to keep the compat paths in sync as these interfaces
continue to develop.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Jan 24 16:33:19 2007 +0000 (2007-01-24)
parents 3f419d160647
children 2a7339251e4d
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 vcpu_guest_context_t ctxt[MAX_VIRT_CPUS];
47 extern int ffsll(long long int);
48 #define FOREACH_CPU(cpumap, i) for ( cpumap = online_cpumap; (i = ffsll(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_t *v)
100 {
101 unsigned long cr0 = v->ctrlreg[0];
102 return (cr0 & X86_CR0_PE) && (cr0 & X86_CR0_PG);
103 }
105 /*
106 * Fetch registers for all online cpus and set the cpumap
107 * to indicate which cpus are online
108 *
109 */
111 static int
112 get_online_cpumap(int xc_handle, struct xen_domctl_getdomaininfo *d,
113 uint64_t *cpumap)
114 {
115 int i, online, retval;
117 *cpumap = 0;
118 for (i = 0; i <= d->max_vcpu_id; i++) {
119 if ((retval = fetch_regs(xc_handle, i, &online)))
120 return retval;
121 if (online)
122 *cpumap |= (1 << i);
123 }
125 return 0;
126 }
128 /*
129 * Notify GDB of any vcpus that have come online or gone offline
130 * update online_cpumap
131 *
132 */
134 static void
135 online_vcpus_changed(uint64_t cpumap)
136 {
137 uint64_t changed_cpumap = cpumap ^ online_cpumap;
138 int index;
140 while ( (index = ffsll(changed_cpumap)) ) {
141 if ( cpumap & (1 << (index - 1)) )
142 {
143 if (handlers.td_create) handlers.td_create(index - 1);
144 } else {
145 IPRINTF("thread death: %d\n", index - 1);
146 if (handlers.td_death) handlers.td_death(index - 1);
147 }
148 changed_cpumap &= ~(1 << (index - 1));
149 }
150 online_cpumap = cpumap;
152 }
154 /* --------------------- */
155 /* XXX application state */
156 static long nr_pages = 0;
157 static uint64_t *page_array = NULL;
160 /*
161 * Translates physical addresses to machine addresses for HVM
162 * guests. For paravirtual domains the function will just return the
163 * given address.
164 *
165 * This function should be used when reading page directories/page
166 * tables.
167 *
168 */
169 static uint64_t
170 to_ma(int cpu, uint64_t maddr)
171 {
172 if ( current_is_hvm && paging_enabled(&ctxt[cpu]) )
173 maddr = page_array[maddr >> PAGE_SHIFT] << PAGE_SHIFT;
174 return maddr;
175 }
177 static void *
178 map_domain_va_32(
179 int xc_handle,
180 int cpu,
181 void *guest_va,
182 int perm)
183 {
184 unsigned long l2e, l1e, l1p, p, va = (unsigned long)guest_va;
185 uint32_t *l2, *l1;
186 static void *v[MAX_VIRT_CPUS];
188 l2 = xc_map_foreign_range(
189 xc_handle, current_domid, PAGE_SIZE, PROT_READ,
190 xen_cr3_to_pfn(ctxt[cpu].ctrlreg[3]));
191 if ( l2 == NULL )
192 return NULL;
194 l2e = l2[l2_table_offset_i386(va)];
195 munmap(l2, PAGE_SIZE);
196 if ( !(l2e & _PAGE_PRESENT) )
197 return NULL;
198 l1p = to_ma(cpu, l2e);
199 l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT);
200 if ( l1 == NULL )
201 return NULL;
203 l1e = l1[l1_table_offset_i386(va)];
204 munmap(l1, PAGE_SIZE);
205 if ( !(l1e & _PAGE_PRESENT) )
206 return NULL;
207 p = to_ma(cpu, l1e);
208 if ( v[cpu] != NULL )
209 munmap(v[cpu], PAGE_SIZE);
210 v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT);
211 if ( v[cpu] == NULL )
212 return NULL;
214 return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1)));
215 }
218 static void *
219 map_domain_va_pae(
220 int xc_handle,
221 int cpu,
222 void *guest_va,
223 int perm)
224 {
225 uint64_t l3e, l2e, l1e, l2p, l1p, p;
226 unsigned long va = (unsigned long)guest_va;
227 uint64_t *l3, *l2, *l1;
228 static void *v[MAX_VIRT_CPUS];
230 l3 = xc_map_foreign_range(
231 xc_handle, current_domid, PAGE_SIZE, PROT_READ,
232 xen_cr3_to_pfn(ctxt[cpu].ctrlreg[3]));
233 if ( l3 == NULL )
234 return NULL;
236 l3e = l3[l3_table_offset_pae(va)];
237 munmap(l3, PAGE_SIZE);
238 if ( !(l3e & _PAGE_PRESENT) )
239 return NULL;
240 l2p = to_ma(cpu, l3e);
241 l2 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l2p >> PAGE_SHIFT);
242 if ( l2 == NULL )
243 return NULL;
245 l2e = l2[l2_table_offset_pae(va)];
246 munmap(l2, PAGE_SIZE);
247 if ( !(l2e & _PAGE_PRESENT) )
248 return NULL;
249 l1p = to_ma(cpu, l2e);
250 l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT);
251 if ( l1 == NULL )
252 return NULL;
254 l1e = l1[l1_table_offset_pae(va)];
255 munmap(l1, PAGE_SIZE);
256 if ( !(l1e & _PAGE_PRESENT) )
257 return NULL;
258 p = to_ma(cpu, l1e);
259 if ( v[cpu] != NULL )
260 munmap(v[cpu], PAGE_SIZE);
261 v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT);
262 if ( v[cpu] == NULL )
263 return NULL;
265 return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1)));
266 }
268 #ifdef __x86_64__
269 static void *
270 map_domain_va_64(
271 int xc_handle,
272 int cpu,
273 void *guest_va,
274 int perm)
275 {
276 unsigned long l4e, l3e, l2e, l1e, l3p, l2p, l1p, p, va = (unsigned long)guest_va;
277 uint64_t *l4, *l3, *l2, *l1;
278 static void *v[MAX_VIRT_CPUS];
280 if ((ctxt[cpu].ctrlreg[4] & 0x20) == 0 ) /* legacy ia32 mode */
281 return map_domain_va_32(xc_handle, cpu, guest_va, perm);
283 l4 = xc_map_foreign_range(
284 xc_handle, current_domid, PAGE_SIZE, PROT_READ,
285 xen_cr3_to_pfn(ctxt[cpu].ctrlreg[3]));
286 if ( l4 == NULL )
287 return NULL;
289 l4e = l4[l4_table_offset(va)];
290 munmap(l4, PAGE_SIZE);
291 if ( !(l4e & _PAGE_PRESENT) )
292 return NULL;
293 l3p = to_ma(cpu, l4e);
294 l3 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l3p >> PAGE_SHIFT);
295 if ( l3 == NULL )
296 return NULL;
298 l3e = l3[l3_table_offset(va)];
299 munmap(l3, PAGE_SIZE);
300 if ( !(l3e & _PAGE_PRESENT) )
301 return NULL;
302 l2p = to_ma(cpu, l3e);
303 l2 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l2p >> PAGE_SHIFT);
304 if ( l2 == NULL )
305 return NULL;
307 l2e = l2[l2_table_offset(va)];
308 munmap(l2, PAGE_SIZE);
309 if ( !(l2e & _PAGE_PRESENT) )
310 return NULL;
311 l1p = to_ma(cpu, l2e);
312 if (l2e & 0x80) { /* 2M pages */
313 p = to_ma(cpu, (l1p + l1_table_offset(va)) << PAGE_SHIFT);
314 } else { /* 4K pages */
315 l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l1p >> PAGE_SHIFT);
316 if ( l1 == NULL )
317 return NULL;
319 l1e = l1[l1_table_offset(va)];
320 munmap(l1, PAGE_SIZE);
321 if ( !(l1e & _PAGE_PRESENT) )
322 return NULL;
323 p = to_ma(cpu, l1e);
324 }
325 if ( v[cpu] != NULL )
326 munmap(v[cpu], PAGE_SIZE);
327 v[cpu] = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p >> PAGE_SHIFT);
328 if ( v[cpu] == NULL )
329 return NULL;
331 return (void *)((unsigned long)v[cpu] | (va & (PAGE_SIZE - 1)));
332 }
333 #endif
335 static void *
336 map_domain_va(
337 int xc_handle,
338 int cpu,
339 void *guest_va,
340 int perm)
341 {
342 unsigned long va = (unsigned long) guest_va;
343 long npgs = xc_get_tot_pages(xc_handle, current_domid);
344 static enum { MODE_UNKNOWN, MODE_64, MODE_32, MODE_PAE } mode;
346 if ( mode == MODE_UNKNOWN )
347 {
348 xen_capabilities_info_t caps;
349 (void)xc_version(xc_handle, XENVER_capabilities, caps);
350 if ( strstr(caps, "-x86_64") )
351 mode = MODE_64;
352 else if ( strstr(caps, "-x86_32p") )
353 mode = MODE_PAE;
354 else if ( strstr(caps, "-x86_32") )
355 mode = MODE_32;
356 }
358 if ( nr_pages != npgs )
359 {
360 if ( nr_pages > 0 )
361 free(page_array);
362 nr_pages = npgs;
363 if ( (page_array = malloc(nr_pages * sizeof(*page_array))) == NULL )
364 {
365 IPRINTF("Could not allocate memory\n");
366 return NULL;
367 }
368 if ( xc_get_pfn_list(xc_handle, current_domid,
369 page_array, nr_pages) != nr_pages )
370 {
371 IPRINTF("Could not get the page frame list\n");
372 return NULL;
373 }
374 }
376 if (fetch_regs(xc_handle, cpu, NULL))
377 return NULL;
379 if (!paging_enabled(&ctxt[cpu])) {
380 static void * v;
381 uint64_t page;
383 if ( v != NULL )
384 munmap(v, PAGE_SIZE);
386 page = to_ma(cpu, va);
388 v = xc_map_foreign_range( xc_handle, current_domid, PAGE_SIZE,
389 perm, page >> PAGE_SHIFT);
391 if ( v == NULL )
392 return NULL;
394 return (void *)(((unsigned long)v) | (va & BSD_PAGE_MASK));
395 }
396 #ifdef __x86_64__
397 if ( mode == MODE_64 )
398 return map_domain_va_64(xc_handle, cpu, guest_va, perm);
399 #endif
400 if ( mode == MODE_PAE )
401 return map_domain_va_pae(xc_handle, cpu, guest_va, perm);
402 /* else ( mode == MODE_32 ) */
403 return map_domain_va_32(xc_handle, cpu, guest_va, perm);
404 }
406 int control_c_pressed_flag = 0;
408 static int
409 __xc_waitdomain(
410 int xc_handle,
411 int domain,
412 int *status,
413 int options)
414 {
415 DECLARE_DOMCTL;
416 int retval;
417 struct timespec ts;
418 uint64_t cpumap;
420 ts.tv_sec = 0;
421 ts.tv_nsec = 10*1000*1000;
423 domctl.cmd = XEN_DOMCTL_getdomaininfo;
424 domctl.domain = domain;
426 retry:
427 retval = do_domctl(xc_handle, &domctl);
428 if ( retval || (domctl.domain != domain) )
429 {
430 IPRINTF("getdomaininfo failed\n");
431 goto done;
432 }
433 *status = domctl.u.getdomaininfo.flags;
435 if ( options & WNOHANG )
436 goto done;
438 if (control_c_pressed_flag) {
439 xc_domain_pause(xc_handle, domain);
440 control_c_pressed_flag = 0;
441 goto done;
442 }
444 if ( !(domctl.u.getdomaininfo.flags & XEN_DOMINF_paused) )
445 {
446 nanosleep(&ts,NULL);
447 goto retry;
448 }
449 done:
450 if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap))
451 IPRINTF("get_online_cpumap failed\n");
452 if (online_cpumap != cpumap)
453 online_vcpus_changed(cpumap);
454 return retval;
456 }
459 long
460 xc_ptrace(
461 int xc_handle,
462 enum __ptrace_request request,
463 uint32_t domid_tid,
464 long eaddr,
465 long edata)
466 {
467 DECLARE_DOMCTL;
468 struct gdb_regs pt;
469 long retval = 0;
470 unsigned long *guest_va;
471 uint64_t cpumap;
472 int cpu, index;
473 void *addr = (char *)eaddr;
474 void *data = (char *)edata;
476 cpu = (request != PTRACE_ATTACH) ? domid_tid : 0;
478 switch ( request )
479 {
480 case PTRACE_PEEKTEXT:
481 case PTRACE_PEEKDATA:
482 if (current_isfile)
483 guest_va = (unsigned long *)map_domain_va_core(
484 current_domid, cpu, addr, ctxt);
485 else
486 guest_va = (unsigned long *)map_domain_va(
487 xc_handle, cpu, addr, PROT_READ);
488 if ( guest_va == NULL )
489 goto out_error;
490 retval = *guest_va;
491 break;
493 case PTRACE_POKETEXT:
494 case PTRACE_POKEDATA:
495 /* XXX assume that all CPUs have the same address space */
496 if (current_isfile)
497 guest_va = (unsigned long *)map_domain_va_core(
498 current_domid, cpu, addr, ctxt);
499 else
500 guest_va = (unsigned long *)map_domain_va(
501 xc_handle, cpu, addr, PROT_READ|PROT_WRITE);
502 if ( guest_va == NULL )
503 goto out_error;
504 *guest_va = (unsigned long)data;
505 break;
507 case PTRACE_GETREGS:
508 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
509 goto out_error;
510 SET_PT_REGS(pt, ctxt[cpu].user_regs);
511 memcpy(data, &pt, sizeof(struct gdb_regs));
512 break;
514 case PTRACE_GETFPREGS:
515 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
516 goto out_error;
517 memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof (elf_fpregset_t));
518 break;
520 case PTRACE_GETFPXREGS:
521 if (!current_isfile && fetch_regs(xc_handle, cpu, NULL))
522 goto out_error;
523 memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof(ctxt[cpu].fpu_ctxt));
524 break;
526 case PTRACE_SETREGS:
527 if (current_isfile)
528 goto out_unsupported; /* XXX not yet supported */
529 SET_XC_REGS(((struct gdb_regs *)data), ctxt[cpu].user_regs);
530 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
531 &ctxt[cpu])))
532 goto out_error_domctl;
533 break;
535 case PTRACE_SINGLESTEP:
536 if (current_isfile)
537 goto out_unsupported; /* XXX not yet supported */
538 /* XXX we can still have problems if the user switches threads
539 * during single-stepping - but that just seems retarded
540 */
541 ctxt[cpu].user_regs.eflags |= PSL_T;
542 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid, cpu,
543 &ctxt[cpu])))
544 goto out_error_domctl;
545 /* FALLTHROUGH */
547 case PTRACE_CONT:
548 case PTRACE_DETACH:
549 if (current_isfile)
550 goto out_unsupported; /* XXX not yet supported */
551 if ( request != PTRACE_SINGLESTEP )
552 {
553 FOREACH_CPU(cpumap, index) {
554 cpu = index - 1;
555 if (fetch_regs(xc_handle, cpu, NULL))
556 goto out_error;
557 /* Clear trace flag */
558 if ( ctxt[cpu].user_regs.eflags & PSL_T )
559 {
560 ctxt[cpu].user_regs.eflags &= ~PSL_T;
561 if ((retval = xc_vcpu_setcontext(xc_handle, current_domid,
562 cpu, &ctxt[cpu])))
563 goto out_error_domctl;
564 }
565 }
566 }
567 if ( request == PTRACE_DETACH )
568 {
569 domctl.cmd = XEN_DOMCTL_setdebugging;
570 domctl.domain = current_domid;
571 domctl.u.setdebugging.enable = 0;
572 if ((retval = do_domctl(xc_handle, &domctl)))
573 goto out_error_domctl;
574 }
575 regs_valid = 0;
576 if ((retval = xc_domain_unpause(xc_handle, current_domid > 0 ?
577 current_domid : -current_domid)))
578 goto out_error_domctl;
579 break;
581 case PTRACE_ATTACH:
582 current_domid = domid_tid;
583 current_isfile = (int)edata;
584 if (current_isfile)
585 break;
586 domctl.cmd = XEN_DOMCTL_getdomaininfo;
587 domctl.domain = current_domid;
588 retval = do_domctl(xc_handle, &domctl);
589 if ( retval || (domctl.domain != current_domid) )
590 goto out_error_domctl;
591 if ( domctl.u.getdomaininfo.flags & XEN_DOMINF_paused )
592 IPRINTF("domain currently paused\n");
593 else if ((retval = xc_domain_pause(xc_handle, current_domid)))
594 goto out_error_domctl;
595 current_is_hvm = !!(domctl.u.getdomaininfo.flags&XEN_DOMINF_hvm_guest);
596 domctl.cmd = XEN_DOMCTL_setdebugging;
597 domctl.domain = current_domid;
598 domctl.u.setdebugging.enable = 1;
599 if ((retval = do_domctl(xc_handle, &domctl)))
600 goto out_error_domctl;
602 if (get_online_cpumap(xc_handle, &domctl.u.getdomaininfo, &cpumap))
603 IPRINTF("get_online_cpumap failed\n");
604 if (online_cpumap != cpumap)
605 online_vcpus_changed(cpumap);
606 break;
608 case PTRACE_TRACEME:
609 IPRINTF("PTRACE_TRACEME is an invalid request under Xen\n");
610 goto out_error;
612 default:
613 goto out_unsupported; /* XXX not yet supported */
614 }
616 return retval;
618 out_error_domctl:
619 perror("domctl failed");
620 out_error:
621 errno = EINVAL;
622 return retval;
624 out_unsupported:
625 #ifdef DEBUG
626 IPRINTF("unsupported xc_ptrace request %s\n", ptrace_names[request]);
627 #endif
628 errno = ENOSYS;
629 return -1;
631 }
633 int
634 xc_waitdomain(
635 int xc_handle,
636 int domain,
637 int *status,
638 int options)
639 {
640 if (current_isfile)
641 return xc_waitdomain_core(xc_handle, domain, status, options, ctxt);
642 return __xc_waitdomain(xc_handle, domain, status, options);
643 }
645 /*
646 * Local variables:
647 * mode: C
648 * c-set-style: "BSD"
649 * c-basic-offset: 4
650 * tab-width: 4
651 * indent-tabs-mode: nil
652 * End:
653 */