debuggers.hg

view tools/libxc/xc_ptrace.c @ 19826:2f9e1348aa98

x86_64: allow more vCPU-s per guest

Since the shared info layout is fixed, guests are required to use
VCPUOP_register_vcpu_info prior to booting any vCPU beyond the
traditional limit of 32.

MAX_VIRT_CPUS, being an implemetation detail of the hypervisor, is no
longer being exposed in the public headers.

The tools changes are clearly incomplete (and done only so things
would
build again), and the current state of the tools (using scalar
variables all over the place to represent vCPU bitmaps) very likely
doesn't permit booting DomU-s with more than the traditional number of
vCPU-s. Testing of the extended functionality was done with Dom0 (96
vCPU-s, as well as 128 vCPU-s out of which the kernel elected - by way
of a simple kernel side patch - to use only some, resulting in a
sparse
bitmap).

ia64 changes only to make things build, and build-tested only (and the
tools part only as far as the build would go without encountering
unrelated problems in the blktap code).

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 18 10:14:16 2009 +0100 (2009-06-18)
parents 9559343fe5e8
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 */