ia64/xen-unstable

view xen/common/domain.c @ 8468:d966b7a00959

Allow non-privileged domains restricted access to
I/O memory and physical interrupts, under control
of domain0. Capabilities are maintained as rangesets
in Xen.

Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil>
Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Sat Dec 31 14:15:22 2005 +0100 (2005-12-31)
parents 4369fd869f51
children 84cf56328ce0
line source
1 /******************************************************************************
2 * domain.c
3 *
4 * Generic domain-handling functions.
5 */
7 #include <xen/config.h>
8 #include <xen/init.h>
9 #include <xen/lib.h>
10 #include <xen/errno.h>
11 #include <xen/sched.h>
12 #include <xen/domain.h>
13 #include <xen/mm.h>
14 #include <xen/event.h>
15 #include <xen/time.h>
16 #include <xen/console.h>
17 #include <xen/softirq.h>
18 #include <xen/domain_page.h>
19 #include <xen/rangeset.h>
20 #include <asm/debugger.h>
21 #include <public/dom0_ops.h>
22 #include <public/sched.h>
23 #include <public/vcpu.h>
25 /* Both these structures are protected by the domlist_lock. */
26 rwlock_t domlist_lock = RW_LOCK_UNLOCKED;
27 struct domain *domain_hash[DOMAIN_HASH_SIZE];
28 struct domain *domain_list;
30 struct domain *dom0;
32 struct domain *do_createdomain(domid_t dom_id, unsigned int cpu)
33 {
34 struct domain *d, **pd;
35 struct vcpu *v;
37 if ( (d = alloc_domain()) == NULL )
38 return NULL;
40 d->domain_id = dom_id;
42 atomic_set(&d->refcnt, 1);
44 spin_lock_init(&d->big_lock);
45 spin_lock_init(&d->page_alloc_lock);
46 INIT_LIST_HEAD(&d->page_list);
47 INIT_LIST_HEAD(&d->xenpage_list);
49 if ( d->domain_id == IDLE_DOMAIN_ID )
50 set_bit(_DOMF_idle_domain, &d->domain_flags);
51 else
52 set_bit(_DOMF_ctrl_pause, &d->domain_flags);
54 if ( !is_idle_task(d) &&
55 ((evtchn_init(d) != 0) || (grant_table_create(d) != 0)) )
56 goto fail1;
58 if ( (v = alloc_vcpu(d, 0, cpu)) == NULL )
59 goto fail2;
61 rangeset_domain_initialise(d);
63 d->iomem_caps = rangeset_new(d, "I/O Memory", RANGESETF_prettyprint_hex);
64 d->irq_caps = rangeset_new(d, "Interrupts", 0);
66 if ( (d->iomem_caps == NULL) ||
67 (d->irq_caps == NULL) ||
68 (arch_do_createdomain(v) != 0) )
69 goto fail3;
71 if ( !is_idle_task(d) )
72 {
73 write_lock(&domlist_lock);
74 pd = &domain_list; /* NB. domain_list maintained in order of dom_id. */
75 for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list )
76 if ( (*pd)->domain_id > d->domain_id )
77 break;
78 d->next_in_list = *pd;
79 *pd = d;
80 d->next_in_hashbucket = domain_hash[DOMAIN_HASH(dom_id)];
81 domain_hash[DOMAIN_HASH(dom_id)] = d;
82 write_unlock(&domlist_lock);
83 }
85 return d;
87 fail3:
88 rangeset_domain_destroy(d);
89 fail2:
90 grant_table_destroy(d);
91 fail1:
92 evtchn_destroy(d);
93 free_domain(d);
94 return NULL;
95 }
98 struct domain *find_domain_by_id(domid_t dom)
99 {
100 struct domain *d;
102 read_lock(&domlist_lock);
103 d = domain_hash[DOMAIN_HASH(dom)];
104 while ( d != NULL )
105 {
106 if ( d->domain_id == dom )
107 {
108 if ( unlikely(!get_domain(d)) )
109 d = NULL;
110 break;
111 }
112 d = d->next_in_hashbucket;
113 }
114 read_unlock(&domlist_lock);
116 return d;
117 }
120 void domain_kill(struct domain *d)
121 {
122 struct vcpu *v;
124 domain_pause(d);
125 if ( !test_and_set_bit(_DOMF_dying, &d->domain_flags) )
126 {
127 for_each_vcpu(d, v)
128 sched_rem_domain(v);
129 domain_relinquish_resources(d);
130 gnttab_release_mappings(d);
131 put_domain(d);
133 send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
134 }
135 }
138 void domain_crash(struct domain *d)
139 {
140 if ( d == current->domain )
141 {
142 printk("Domain %d (vcpu#%d) crashed on cpu#%d:\n",
143 d->domain_id, current->vcpu_id, smp_processor_id());
144 show_registers(guest_cpu_user_regs());
145 }
146 else
147 {
148 printk("Domain %d reported crashed by domain %d on cpu#%d:\n",
149 d->domain_id, current->domain->domain_id, smp_processor_id());
150 }
152 domain_shutdown(d, SHUTDOWN_crash);
153 }
156 void domain_crash_synchronous(void)
157 {
158 domain_crash(current->domain);
159 for ( ; ; )
160 do_softirq();
161 }
164 static struct domain *domain_shuttingdown[NR_CPUS];
166 static void domain_shutdown_finalise(void)
167 {
168 struct domain *d;
169 struct vcpu *v;
171 d = domain_shuttingdown[smp_processor_id()];
172 domain_shuttingdown[smp_processor_id()] = NULL;
174 BUG_ON(d == NULL);
175 BUG_ON(d == current->domain);
176 BUG_ON(!test_bit(_DOMF_shuttingdown, &d->domain_flags));
177 BUG_ON(test_bit(_DOMF_shutdown, &d->domain_flags));
179 /* Make sure that every vcpu is descheduled before we finalise. */
180 for_each_vcpu ( d, v )
181 vcpu_sleep_sync(v);
182 BUG_ON(!cpus_empty(d->cpumask));
184 sync_pagetable_state(d);
186 set_bit(_DOMF_shutdown, &d->domain_flags);
187 clear_bit(_DOMF_shuttingdown, &d->domain_flags);
189 send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
190 }
192 static __init int domain_shutdown_finaliser_init(void)
193 {
194 open_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ, domain_shutdown_finalise);
195 return 0;
196 }
197 __initcall(domain_shutdown_finaliser_init);
200 void domain_shutdown(struct domain *d, u8 reason)
201 {
202 struct vcpu *v;
204 if ( d->domain_id == 0 )
205 {
206 extern void machine_restart(char *);
207 extern void machine_halt(void);
209 debugger_trap_immediate();
211 if ( reason == SHUTDOWN_poweroff )
212 {
213 printk("Domain 0 halted: halting machine.\n");
214 machine_halt();
215 }
216 else
217 {
218 printk("Domain 0 shutdown: rebooting machine.\n");
219 machine_restart(0);
220 }
221 }
223 /* Mark the domain as shutting down. */
224 d->shutdown_code = reason;
225 if ( !test_and_set_bit(_DOMF_shuttingdown, &d->domain_flags) )
226 {
227 /* This vcpu won the race to finalise the shutdown. */
228 domain_shuttingdown[smp_processor_id()] = d;
229 raise_softirq(DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ);
230 }
232 /* Put every vcpu to sleep, but don't wait (avoids inter-vcpu deadlock). */
233 for_each_vcpu ( d, v )
234 vcpu_sleep_nosync(v);
235 }
238 void domain_pause_for_debugger(void)
239 {
240 struct domain *d = current->domain;
241 struct vcpu *v;
243 /*
244 * NOTE: This does not synchronously pause the domain. The debugger
245 * must issue a PAUSEDOMAIN command to ensure that all execution
246 * has ceased and guest state is committed to memory.
247 */
248 set_bit(_DOMF_ctrl_pause, &d->domain_flags);
249 for_each_vcpu ( d, v )
250 vcpu_sleep_nosync(v);
252 send_guest_virq(dom0->vcpu[0], VIRQ_DEBUGGER);
253 }
256 /* Release resources belonging to task @p. */
257 void domain_destruct(struct domain *d)
258 {
259 struct domain **pd;
260 atomic_t old, new;
262 BUG_ON(!test_bit(_DOMF_dying, &d->domain_flags));
264 /* May be already destructed, or get_domain() can race us. */
265 _atomic_set(old, 0);
266 _atomic_set(new, DOMAIN_DESTRUCTED);
267 old = atomic_compareandswap(old, new, &d->refcnt);
268 if ( _atomic_read(old) != 0 )
269 return;
271 /* Delete from task list and task hashtable. */
272 write_lock(&domlist_lock);
273 pd = &domain_list;
274 while ( *pd != d )
275 pd = &(*pd)->next_in_list;
276 *pd = d->next_in_list;
277 pd = &domain_hash[DOMAIN_HASH(d->domain_id)];
278 while ( *pd != d )
279 pd = &(*pd)->next_in_hashbucket;
280 *pd = d->next_in_hashbucket;
281 write_unlock(&domlist_lock);
283 rangeset_domain_destroy(d);
285 evtchn_destroy(d);
286 grant_table_destroy(d);
288 free_perdomain_pt(d);
289 free_xenheap_page(d->shared_info);
291 free_domain(d);
293 send_guest_virq(dom0->vcpu[0], VIRQ_DOM_EXC);
294 }
296 void vcpu_pause(struct vcpu *v)
297 {
298 BUG_ON(v == current);
299 atomic_inc(&v->pausecnt);
300 vcpu_sleep_sync(v);
301 }
303 void domain_pause(struct domain *d)
304 {
305 struct vcpu *v;
307 for_each_vcpu( d, v )
308 vcpu_pause(v);
310 sync_pagetable_state(d);
311 }
313 void vcpu_unpause(struct vcpu *v)
314 {
315 BUG_ON(v == current);
316 if ( atomic_dec_and_test(&v->pausecnt) )
317 vcpu_wake(v);
318 }
320 void domain_unpause(struct domain *d)
321 {
322 struct vcpu *v;
324 for_each_vcpu( d, v )
325 vcpu_unpause(v);
326 }
328 void domain_pause_by_systemcontroller(struct domain *d)
329 {
330 struct vcpu *v;
332 BUG_ON(current->domain == d);
334 if ( !test_and_set_bit(_DOMF_ctrl_pause, &d->domain_flags) )
335 {
336 for_each_vcpu ( d, v )
337 vcpu_sleep_sync(v);
338 }
340 sync_pagetable_state(d);
341 }
343 void domain_unpause_by_systemcontroller(struct domain *d)
344 {
345 struct vcpu *v;
347 if ( test_and_clear_bit(_DOMF_ctrl_pause, &d->domain_flags) )
348 {
349 for_each_vcpu ( d, v )
350 vcpu_wake(v);
351 }
352 }
355 /*
356 * set_info_guest is used for final setup, launching, and state modification
357 * of domains other than domain 0. ie. the domains that are being built by
358 * the userspace dom0 domain builder.
359 */
360 int set_info_guest(struct domain *d, dom0_setdomaininfo_t *setdomaininfo)
361 {
362 int rc = 0;
363 struct vcpu_guest_context *c = NULL;
364 unsigned long vcpu = setdomaininfo->vcpu;
365 struct vcpu *v;
367 if ( (vcpu >= MAX_VIRT_CPUS) || ((v = d->vcpu[vcpu]) == NULL) )
368 return -EINVAL;
370 if ( !test_bit(_DOMF_ctrl_pause, &d->domain_flags) )
371 return -EINVAL;
373 if ( (c = xmalloc(struct vcpu_guest_context)) == NULL )
374 return -ENOMEM;
376 rc = -EFAULT;
377 if ( copy_from_user(c, setdomaininfo->ctxt, sizeof(*c)) == 0 )
378 rc = arch_set_info_guest(v, c);
380 xfree(c);
381 return rc;
382 }
384 int boot_vcpu(struct domain *d, int vcpuid, struct vcpu_guest_context *ctxt)
385 {
386 struct vcpu *v = d->vcpu[vcpuid];
388 BUG_ON(test_bit(_VCPUF_initialised, &v->vcpu_flags));
390 return arch_set_info_guest(v, ctxt);
391 }
393 long do_vcpu_op(int cmd, int vcpuid, void *arg)
394 {
395 struct domain *d = current->domain;
396 struct vcpu *v;
397 struct vcpu_guest_context *ctxt;
398 long rc = 0;
400 if ( (vcpuid < 0) || (vcpuid >= MAX_VIRT_CPUS) )
401 return -EINVAL;
403 if ( (v = d->vcpu[vcpuid]) == NULL )
404 return -ENOENT;
406 switch ( cmd )
407 {
408 case VCPUOP_initialise:
409 if ( (ctxt = xmalloc(struct vcpu_guest_context)) == NULL )
410 {
411 rc = -ENOMEM;
412 break;
413 }
415 if ( copy_from_user(ctxt, arg, sizeof(*ctxt)) )
416 {
417 xfree(ctxt);
418 rc = -EFAULT;
419 break;
420 }
422 LOCK_BIGLOCK(d);
423 rc = -EEXIST;
424 if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
425 rc = boot_vcpu(d, vcpuid, ctxt);
426 UNLOCK_BIGLOCK(d);
428 xfree(ctxt);
429 break;
431 case VCPUOP_up:
432 if ( !test_bit(_VCPUF_initialised, &v->vcpu_flags) )
433 rc = -EINVAL;
434 else if ( test_and_clear_bit(_VCPUF_down, &v->vcpu_flags) )
435 vcpu_wake(v);
436 break;
438 case VCPUOP_down:
439 if ( !test_and_set_bit(_VCPUF_down, &v->vcpu_flags) )
440 vcpu_sleep_nosync(v);
441 break;
443 case VCPUOP_is_up:
444 rc = !test_bit(_VCPUF_down, &v->vcpu_flags);
445 break;
446 }
448 return rc;
449 }
451 long vm_assist(struct domain *p, unsigned int cmd, unsigned int type)
452 {
453 if ( type > MAX_VMASST_TYPE )
454 return -EINVAL;
456 switch ( cmd )
457 {
458 case VMASST_CMD_enable:
459 set_bit(type, &p->vm_assist);
460 return 0;
461 case VMASST_CMD_disable:
462 clear_bit(type, &p->vm_assist);
463 return 0;
464 }
466 return -ENOSYS;
467 }
469 /*
470 * Local variables:
471 * mode: C
472 * c-set-style: "BSD"
473 * c-basic-offset: 4
474 * tab-width: 4
475 * indent-tabs-mode: nil
476 * End:
477 */