ia64/xen-unstable

view tools/libxc/xc_ptrace.c @ 8400:6b1d39a56c2b

Add debugging flag for domains to make domu debugging a run-time option
Signed-off-by: Kip Macy kmacy@fsmware.ckm
author kaf24@firebug.cl.cam.ac.uk
date Thu Dec 15 21:50:12 2005 +0100 (2005-12-15)
parents b5b7a9d9fc56
children dd5649730b32
line source
1 #define XC_PTRACE_PRIVATE
4 #include <sys/ptrace.h>
5 #include <sys/wait.h>
6 #include <time.h>
8 #include "xc_private.h"
9 #include "xg_private.h"
10 #include <thread_db.h>
11 #include "xc_ptrace.h"
14 /* XXX application state */
15 static long nr_pages = 0;
16 static unsigned long *page_array = NULL;
17 static int current_domid = -1;
19 static cpumap_t online_cpumap;
20 static cpumap_t regs_valid;
21 static vcpu_guest_context_t ctxt[MAX_VIRT_CPUS];
23 extern int ffsll(long long int);
24 #define FOREACH_CPU(cpumap, i) for ( cpumap = online_cpumap; (i = ffsll(cpumap)); cpumap &= ~(1 << (index - 1)) )
27 static int
28 fetch_regs(int xc_handle, int cpu, int *online)
29 {
30 xc_vcpuinfo_t info;
31 int retval = 0;
33 if (online)
34 *online = 0;
35 if ( !(regs_valid & (1 << cpu)) ) {
36 retval = xc_domain_get_vcpu_context(xc_handle, current_domid,
37 cpu, &ctxt[cpu]);
38 if ( retval )
39 goto done;
40 regs_valid |= (1 << cpu);
42 }
43 if ( online == NULL )
44 goto done;
46 retval = xc_domain_get_vcpu_info(xc_handle, current_domid,
47 cpu, &info);
48 *online = info.online;
50 done:
51 return retval;
52 }
54 #define FETCH_REGS(cpu) if (fetch_regs(xc_handle, cpu, NULL)) goto error_out;
57 static struct thr_ev_handlers {
58 thr_ev_handler_t td_create;
59 thr_ev_handler_t td_death;
60 } handlers;
62 void
63 xc_register_event_handler(thr_ev_handler_t h,
64 td_event_e e)
65 {
66 switch (e) {
67 case TD_CREATE:
68 handlers.td_create = h;
69 break;
70 case TD_DEATH:
71 handlers.td_death = h;
72 break;
73 default:
74 abort(); /* XXX */
75 }
76 }
78 static inline int
79 paging_enabled(vcpu_guest_context_t *v)
80 {
81 unsigned long cr0 = v->ctrlreg[0];
82 return (cr0 & X86_CR0_PE) && (cr0 & X86_CR0_PG);
83 }
85 /*
86 * Fetch registers for all online cpus and set the cpumap
87 * to indicate which cpus are online
88 *
89 */
91 static int
92 get_online_cpumap(int xc_handle, dom0_getdomaininfo_t *d, cpumap_t *cpumap)
93 {
94 int i, online, retval;
96 *cpumap = 0;
97 for (i = 0; i <= d->max_vcpu_id; i++) {
98 if ((retval = fetch_regs(xc_handle, i, &online)))
99 goto error_out;
100 if (online)
101 *cpumap |= (1 << i);
102 }
104 return 0;
105 error_out:
106 return retval;
107 }
109 /*
110 * Notify GDB of any vcpus that have come online or gone offline
111 * update online_cpumap
112 *
113 */
115 static void
116 online_vcpus_changed(cpumap_t cpumap)
117 {
118 cpumap_t changed_cpumap = cpumap ^ online_cpumap;
119 int index;
121 while ( (index = ffsll(changed_cpumap)) ) {
122 if ( cpumap & (1 << (index - 1)) ) {
123 if (handlers.td_create) handlers.td_create(index - 1);
124 } else {
125 printf("thread death: %d\n", index - 1);
126 if (handlers.td_death) handlers.td_death(index - 1);
127 }
128 changed_cpumap &= ~(1 << (index - 1));
129 }
130 online_cpumap = cpumap;
132 }
134 /* --------------------- */
136 static void *
137 map_domain_va_pae(
138 int xc_handle,
139 int cpu,
140 void *guest_va,
141 int perm)
142 {
143 unsigned long l2p, l1p, p, va = (unsigned long)guest_va;
144 uint64_t *l3, *l2, *l1;
145 static void *v;
147 FETCH_REGS(cpu);
149 l3 = xc_map_foreign_range(
150 xc_handle, current_domid, PAGE_SIZE, PROT_READ, ctxt[cpu].ctrlreg[3] >> PAGE_SHIFT);
151 if ( l3 == NULL )
152 goto error_out;
154 l2p = l3[l3_table_offset_pae(va)] >> PAGE_SHIFT;
155 l2 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, PROT_READ, l2p);
156 if ( l2 == NULL )
157 goto error_out;
159 l1p = l2[l2_table_offset_pae(va)] >> PAGE_SHIFT;
160 l1 = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, l1p);
161 if ( l1 == NULL )
162 goto error_out;
164 p = l1[l1_table_offset_pae(va)] >> PAGE_SHIFT;
165 if ( v != NULL )
166 munmap(v, PAGE_SIZE);
167 v = xc_map_foreign_range(xc_handle, current_domid, PAGE_SIZE, perm, p);
168 if ( v == NULL )
169 goto error_out;
171 return (void *)((unsigned long)v | (va & (PAGE_SIZE - 1)));
173 error_out:
174 return NULL;
175 }
177 static void *
178 map_domain_va(
179 int xc_handle,
180 int cpu,
181 void *guest_va,
182 int perm)
183 {
185 unsigned long pde, page;
186 unsigned long va = (unsigned long)guest_va;
187 long npgs = xc_get_tot_pages(xc_handle, current_domid);
190 static uint32_t cr3_phys[MAX_VIRT_CPUS];
191 static unsigned long *cr3_virt[MAX_VIRT_CPUS];
192 static unsigned long pde_phys[MAX_VIRT_CPUS];
193 static unsigned long *pde_virt[MAX_VIRT_CPUS];
194 static unsigned long page_phys[MAX_VIRT_CPUS];
195 static unsigned long *page_virt[MAX_VIRT_CPUS];
196 static int prev_perm[MAX_VIRT_CPUS];
197 static enum { MODE_UNKNOWN, MODE_32, MODE_PAE } mode;
199 if ( mode == MODE_UNKNOWN )
200 {
201 xen_capabilities_info_t caps;
202 (void)xc_version(xc_handle, XENVER_capabilities, caps);
203 mode = MODE_32;
204 if ( strstr(caps, "_x86_32p") )
205 mode = MODE_PAE;
206 }
208 if ( mode == MODE_PAE )
209 return map_domain_va_pae(xc_handle, cpu, guest_va, perm);
211 if ( nr_pages != npgs )
212 {
213 if ( nr_pages > 0 )
214 free(page_array);
215 nr_pages = npgs;
216 if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
217 {
218 printf("Could not allocate memory\n");
219 goto error_out;
220 }
221 if ( xc_get_pfn_list(xc_handle, current_domid,
222 page_array, nr_pages) != nr_pages )
223 {
224 printf("Could not get the page frame list\n");
225 goto error_out;
226 }
227 }
229 FETCH_REGS(cpu);
231 if ( ctxt[cpu].ctrlreg[3] != cr3_phys[cpu] )
232 {
233 cr3_phys[cpu] = ctxt[cpu].ctrlreg[3];
234 if ( cr3_virt[cpu] )
235 munmap(cr3_virt[cpu], PAGE_SIZE);
236 cr3_virt[cpu] = xc_map_foreign_range(
237 xc_handle, current_domid, PAGE_SIZE, PROT_READ,
238 cr3_phys[cpu] >> PAGE_SHIFT);
239 if ( cr3_virt[cpu] == NULL )
240 goto error_out;
241 }
242 if ( (pde = cr3_virt[cpu][vtopdi(va)]) == 0 )
243 goto error_out;
244 if ( (ctxt[cpu].flags & VGCF_VMX_GUEST) && paging_enabled(&ctxt[cpu]) )
245 pde = page_array[pde >> PAGE_SHIFT] << PAGE_SHIFT;
246 if ( pde != pde_phys[cpu] )
247 {
248 pde_phys[cpu] = pde;
249 if ( pde_virt[cpu] )
250 munmap(pde_virt[cpu], PAGE_SIZE);
251 pde_virt[cpu] = xc_map_foreign_range(
252 xc_handle, current_domid, PAGE_SIZE, PROT_READ,
253 pde_phys[cpu] >> PAGE_SHIFT);
254 if ( pde_virt[cpu] == NULL )
255 goto error_out;
256 }
257 if ( (page = pde_virt[cpu][vtopti(va)]) == 0 )
258 goto error_out;
259 if ( (ctxt[cpu].flags & VGCF_VMX_GUEST) && paging_enabled(&ctxt[cpu]) )
260 page = page_array[page >> PAGE_SHIFT] << PAGE_SHIFT;
261 if ( (page != page_phys[cpu]) || (perm != prev_perm[cpu]) )
262 {
263 page_phys[cpu] = page;
264 if ( page_virt[cpu] )
265 munmap(page_virt[cpu], PAGE_SIZE);
266 page_virt[cpu] = xc_map_foreign_range(
267 xc_handle, current_domid, PAGE_SIZE, perm,
268 page_phys[cpu] >> PAGE_SHIFT);
269 if ( page_virt[cpu] == NULL )
270 {
271 page_phys[cpu] = 0;
272 goto error_out;
273 }
274 prev_perm[cpu] = perm;
275 }
277 return (void *)(((unsigned long)page_virt[cpu]) | (va & BSD_PAGE_MASK));
279 error_out:
280 return NULL;
281 }
283 int
284 xc_waitdomain(
285 int xc_handle,
286 int domain,
287 int *status,
288 int options)
289 {
290 DECLARE_DOM0_OP;
291 int retval;
292 struct timespec ts;
293 cpumap_t cpumap;
295 ts.tv_sec = 0;
296 ts.tv_nsec = 10*1000*1000;
298 op.cmd = DOM0_GETDOMAININFO;
299 op.u.getdomaininfo.domain = domain;
302 retry:
303 retval = do_dom0_op(xc_handle, &op);
304 if ( retval || (op.u.getdomaininfo.domain != domain) )
305 {
306 printf("getdomaininfo failed\n");
307 goto done;
308 }
309 *status = op.u.getdomaininfo.flags;
311 if ( options & WNOHANG )
312 goto done;
314 if ( !(op.u.getdomaininfo.flags & DOMFLAGS_PAUSED) )
315 {
316 nanosleep(&ts,NULL);
317 goto retry;
318 }
319 /* XXX check for ^C here */
320 done:
321 if (get_online_cpumap(xc_handle, &op.u.getdomaininfo, &cpumap))
322 printf("get_online_cpumap failed\n");
323 if (online_cpumap != cpumap)
324 online_vcpus_changed(cpumap);
325 return retval;
327 }
330 long
331 xc_ptrace(
332 int xc_handle,
333 enum __ptrace_request request,
334 uint32_t domid_tid,
335 long eaddr,
336 long edata)
337 {
338 DECLARE_DOM0_OP;
339 int status = 0;
340 struct gdb_regs pt;
341 long retval = 0;
342 unsigned long *guest_va;
343 cpumap_t cpumap;
344 int cpu, index;
345 void *addr = (char *)eaddr;
346 void *data = (char *)edata;
348 cpu = (request != PTRACE_ATTACH) ? domid_tid : 0;
350 switch ( request )
351 {
352 case PTRACE_PEEKTEXT:
353 case PTRACE_PEEKDATA:
354 guest_va = (unsigned long *)map_domain_va(
355 xc_handle, cpu, addr, PROT_READ);
356 if ( guest_va == NULL )
357 {
358 status = EFAULT;
359 goto error_out;
360 }
361 retval = *guest_va;
362 break;
364 case PTRACE_POKETEXT:
365 case PTRACE_POKEDATA:
366 /* XXX assume that all CPUs have the same address space */
367 guest_va = (unsigned long *)map_domain_va(
368 xc_handle, cpu, addr, PROT_READ|PROT_WRITE);
369 if ( guest_va == NULL ) {
370 status = EFAULT;
371 goto error_out;
372 }
373 *guest_va = (unsigned long)data;
374 break;
376 case PTRACE_GETREGS:
377 case PTRACE_GETFPREGS:
378 case PTRACE_GETFPXREGS:
380 FETCH_REGS(cpu);
381 if ( request == PTRACE_GETREGS )
382 {
383 SET_PT_REGS(pt, ctxt[cpu].user_regs);
384 memcpy(data, &pt, sizeof(struct gdb_regs));
385 }
386 else if (request == PTRACE_GETFPREGS)
387 {
388 memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof(ctxt[cpu].fpu_ctxt));
389 }
390 else /*if (request == PTRACE_GETFPXREGS)*/
391 {
392 memcpy(data, &ctxt[cpu].fpu_ctxt, sizeof(ctxt[cpu].fpu_ctxt));
393 }
394 break;
396 case PTRACE_SETREGS:
397 SET_XC_REGS(((struct gdb_regs *)data), ctxt[cpu].user_regs);
398 retval = xc_domain_setinfo(xc_handle, current_domid, cpu, &ctxt[cpu]);
399 if (retval)
400 goto error_out;
401 break;
403 case PTRACE_SINGLESTEP:
404 /* XXX we can still have problems if the user switches threads
405 * during single-stepping - but that just seems retarded
406 */
407 ctxt[cpu].user_regs.eflags |= PSL_T;
408 retval = xc_domain_setinfo(xc_handle, current_domid, cpu, &ctxt[cpu]);
409 if ( retval )
410 {
411 perror("dom0 op failed");
412 goto error_out;
413 }
414 /* FALLTHROUGH */
416 case PTRACE_CONT:
417 case PTRACE_DETACH:
418 if ( request != PTRACE_SINGLESTEP )
419 {
420 FOREACH_CPU(cpumap, index) {
421 cpu = index - 1;
422 FETCH_REGS(cpu);
423 /* Clear trace flag */
424 if ( ctxt[cpu].user_regs.eflags & PSL_T ) {
425 ctxt[cpu].user_regs.eflags &= ~PSL_T;
426 retval = xc_domain_setinfo(xc_handle, current_domid,
427 cpu, &ctxt[cpu]);
428 if ( retval ) {
429 perror("dom0 op failed");
430 goto error_out;
431 }
432 }
433 }
434 }
435 if ( request == PTRACE_DETACH )
436 {
437 op.cmd = DOM0_SETDEBUGGING;
438 op.u.setdebugging.domain = current_domid;
439 op.u.setdebugging.enable = 0;
440 retval = do_dom0_op(xc_handle, &op);
441 }
442 regs_valid = 0;
443 xc_domain_unpause(xc_handle, current_domid > 0 ? current_domid : -current_domid);
444 break;
446 case PTRACE_ATTACH:
447 current_domid = domid_tid;
448 op.cmd = DOM0_GETDOMAININFO;
449 op.u.getdomaininfo.domain = current_domid;
450 retval = do_dom0_op(xc_handle, &op);
451 if ( retval || (op.u.getdomaininfo.domain != current_domid) )
452 {
453 perror("dom0 op failed");
454 goto error_out;
455 }
456 if ( op.u.getdomaininfo.flags & DOMFLAGS_PAUSED )
457 {
458 printf("domain currently paused\n");
459 } else
460 retval = xc_domain_pause(xc_handle, current_domid);
461 op.cmd = DOM0_SETDEBUGGING;
462 op.u.setdebugging.domain = current_domid;
463 op.u.setdebugging.enable = 1;
464 retval = do_dom0_op(xc_handle, &op);
466 if (get_online_cpumap(xc_handle, &op.u.getdomaininfo, &cpumap))
467 printf("get_online_cpumap failed\n");
468 if (online_cpumap != cpumap)
469 online_vcpus_changed(cpumap);
470 break;
472 case PTRACE_SETFPREGS:
473 case PTRACE_SETFPXREGS:
474 case PTRACE_PEEKUSER:
475 case PTRACE_POKEUSER:
476 case PTRACE_SYSCALL:
477 case PTRACE_KILL:
478 #ifdef DEBUG
479 printf("unsupported xc_ptrace request %s\n", ptrace_names[request]);
480 #endif
481 /* XXX not yet supported */
482 status = ENOSYS;
483 break;
485 case PTRACE_TRACEME:
486 printf("PTRACE_TRACEME is an invalid request under Xen\n");
487 status = EINVAL;
488 }
490 if ( status )
491 {
492 errno = status;
493 retval = -1;
494 }
496 error_out:
497 return retval;
498 }
500 /*
501 * Local variables:
502 * mode: C
503 * c-set-style: "BSD"
504 * c-basic-offset: 4
505 * tab-width: 4
506 * indent-tabs-mode: nil
507 * End:
508 */