debuggers.hg

view xen/common/grant_table.c @ 3658:0ef6e8e6e85d

bitkeeper revision 1.1159.212.71 (4200f0afX_JumfbEHQex6TdFENULMQ)

Merge labyrinth.cl.cam.ac.uk:/auto/groups/xeno-xenod/BK/xen-unstable.bk
into labyrinth.cl.cam.ac.uk:/auto/groups/xeno/users/iap10/xeno-clone/xen-unstable.bk
author iap10@labyrinth.cl.cam.ac.uk
date Wed Feb 02 15:24:31 2005 +0000 (2005-02-02)
parents ddf5b350364f beb0887c54bc
children bbe8541361dd 4294cfa9fad3
line source
1 /******************************************************************************
2 * common/grant_table.c
3 *
4 * Mechanism for granting foreign access to page frames, and receiving
5 * page-ownership transfers.
6 *
7 * Copyright (c) 2004 K A Fraser
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
24 #include <xen/config.h>
25 #include <xen/sched.h>
27 #define PIN_FAIL(_rc, _f, _a...) \
28 do { \
29 DPRINTK( _f, ## _a ); \
30 rc = (_rc); \
31 goto fail; \
32 } while ( 0 )
34 static inline int
35 get_maptrack_handle(
36 grant_table_t *t)
37 {
38 unsigned int h;
39 if ( unlikely((h = t->maptrack_head) == NR_MAPTRACK_ENTRIES) )
40 return -1;
41 t->maptrack_head = t->maptrack[h].ref_and_flags >> MAPTRACK_REF_SHIFT;
42 return h;
43 }
45 static inline void
46 put_maptrack_handle(
47 grant_table_t *t, int handle)
48 {
49 t->maptrack[handle].ref_and_flags = t->maptrack_head << MAPTRACK_REF_SHIFT;
50 t->maptrack_head = handle;
51 }
53 static void
54 __gnttab_map_grant_ref(
55 gnttab_map_grant_ref_t *uop)
56 {
57 domid_t dom, sdom;
58 grant_ref_t ref;
59 struct domain *ld, *rd;
60 u16 flags, sflags;
61 int handle;
62 active_grant_entry_t *act;
63 grant_entry_t *sha;
64 s16 rc = 0;
65 unsigned long frame;
67 /*
68 * We bound the number of times we retry CMPXCHG on memory locations that
69 * we share with a guest OS. The reason is that the guest can modify that
70 * location at a higher rate than we can read-modify-CMPXCHG, so the guest
71 * could cause us to livelock. There are a few cases where it is valid for
72 * the guest to race our updates (e.g., to change the GTF_readonly flag),
73 * so we allow a few retries before failing.
74 */
75 int retries = 0;
77 ld = current->domain;
79 /* Bitwise-OR avoids short-circuiting which screws control flow. */
80 if ( unlikely(__get_user(dom, &uop->dom) |
81 __get_user(ref, &uop->ref) |
82 __get_user(flags, &uop->flags)) )
83 {
84 DPRINTK("Fault while reading gnttab_map_grant_ref_t.\n");
85 return; /* don't set status */
86 }
88 if ( unlikely(ref >= NR_GRANT_ENTRIES) ||
89 unlikely((flags & (GNTMAP_device_map|GNTMAP_host_map)) == 0) )
90 {
91 DPRINTK("Bad ref (%d) or flags (%x).\n", ref, flags);
92 (void)__put_user(GNTST_bad_gntref, &uop->handle);
93 return;
94 }
96 if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
97 unlikely(ld == rd) )
98 {
99 if ( rd != NULL )
100 put_domain(rd);
101 DPRINTK("Could not find domain %d\n", dom);
102 (void)__put_user(GNTST_bad_domain, &uop->handle);
103 return;
104 }
106 if ( unlikely((handle = get_maptrack_handle(ld->grant_table)) == -1) )
107 {
108 put_domain(rd);
109 DPRINTK("No more map handles available\n");
110 (void)__put_user(GNTST_no_device_space, &uop->handle);
111 return;
112 }
114 act = &rd->grant_table->active[ref];
115 sha = &rd->grant_table->shared[ref];
117 spin_lock(&rd->grant_table->lock);
119 if ( act->pin == 0 )
120 {
121 /* CASE 1: Activating a previously inactive entry. */
123 sflags = sha->flags;
124 sdom = sha->domid;
126 for ( ; ; )
127 {
128 u32 scombo, prev_scombo, new_scombo;
130 if ( unlikely((sflags & GTF_type_mask) != GTF_permit_access) ||
131 unlikely(sdom != ld->id) )
132 PIN_FAIL(GNTST_general_error,
133 "Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
134 sflags, sdom, ld->id);
136 /* Merge two 16-bit values into a 32-bit combined update. */
137 /* NB. Endianness! */
138 prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;
140 new_scombo = scombo | GTF_reading;
141 if ( !(flags & GNTMAP_readonly) )
142 {
143 new_scombo |= GTF_writing;
144 if ( unlikely(sflags & GTF_readonly) )
145 PIN_FAIL(GNTST_general_error,
146 "Attempt to write-pin a r/o grant entry.\n");
147 }
149 /* NB. prev_scombo is updated in place to seen value. */
150 if ( unlikely(cmpxchg_user((u32 *)&sha->flags,
151 prev_scombo,
152 new_scombo)) )
153 PIN_FAIL(GNTST_general_error,
154 "Fault while modifying shared flags and domid.\n");
156 /* Did the combined update work (did we see what we expected?). */
157 if ( likely(prev_scombo == scombo) )
158 break;
160 if ( retries++ == 4 )
161 PIN_FAIL(GNTST_general_error,
162 "Shared grant entry is unstable.\n");
164 /* Didn't see what we expected. Split out the seen flags & dom. */
165 /* NB. Endianness! */
166 sflags = (u16)prev_scombo;
167 sdom = (u16)(prev_scombo >> 16);
168 }
170 /* rmb(); */ /* not on x86 */
171 frame = sha->frame;
172 if ( unlikely(!pfn_is_ram(frame)) ||
173 unlikely(!((flags & GNTMAP_readonly) ?
174 get_page(&frame_table[frame], rd) :
175 get_page_and_type(&frame_table[frame], rd,
176 PGT_writable_page))) )
177 {
178 clear_bit(_GTF_writing, &sha->flags);
179 clear_bit(_GTF_reading, &sha->flags);
180 PIN_FAIL(GNTST_general_error,
181 "Could not pin the granted frame!\n");
182 }
184 if ( flags & GNTMAP_device_map )
185 act->pin += (flags & GNTMAP_readonly) ?
186 GNTPIN_devr_inc : GNTPIN_devw_inc;
187 if ( flags & GNTMAP_host_map )
188 act->pin += (flags & GNTMAP_readonly) ?
189 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
190 act->domid = sdom;
191 act->frame = frame;
192 }
193 else
194 {
195 /* CASE 2: Active modications to an already active entry. */
197 /*
198 * A cheesy check for possible pin-count overflow.
199 * A more accurate check cannot be done with a single comparison.
200 */
201 if ( (act->pin & 0x80808080U) != 0 )
202 PIN_FAIL(ENOSPC, "Risk of counter overflow %08x\n", act->pin);
204 if ( !(flags & GNTMAP_readonly) &&
205 !((sflags = sha->flags) & GTF_writing) )
206 {
207 for ( ; ; )
208 {
209 u16 prev_sflags;
211 if ( unlikely(sflags & GTF_readonly) )
212 PIN_FAIL(GNTST_general_error,
213 "Attempt to write-pin a r/o grant entry.\n");
215 prev_sflags = sflags;
217 /* NB. prev_sflags is updated in place to seen value. */
218 if ( unlikely(cmpxchg_user(&sha->flags, prev_sflags,
219 prev_sflags | GTF_writing)) )
220 PIN_FAIL(GNTST_general_error,
221 "Fault while modifying shared flags.\n");
223 if ( likely(prev_sflags == sflags) )
224 break;
226 if ( retries++ == 4 )
227 PIN_FAIL(GNTST_general_error,
228 "Shared grant entry is unstable.\n");
230 sflags = prev_sflags;
231 }
233 if ( unlikely(!get_page_type(&frame_table[act->frame],
234 PGT_writable_page)) )
235 {
236 clear_bit(_GTF_writing, &sha->flags);
237 PIN_FAIL(GNTST_general_error,
238 "Attempt to write-pin a unwritable page.\n");
239 }
240 }
242 if ( flags & GNTMAP_device_map )
243 act->pin += (flags & GNTMAP_readonly) ?
244 GNTPIN_devr_inc : GNTPIN_devw_inc;
245 if ( flags & GNTMAP_host_map )
246 act->pin += (flags & GNTMAP_readonly) ?
247 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
248 }
250 ld->grant_table->maptrack[handle].domid = dom;
251 ld->grant_table->maptrack[handle].ref_and_flags =
252 (ref << MAPTRACK_REF_SHIFT) | (flags & MAPTRACK_GNTMAP_MASK);
254 /* Unchecked and unconditional. */
255 (void)__put_user(handle, &uop->handle);
256 (void)__put_user(act->frame, &uop->dev_bus_addr);
258 spin_unlock(&rd->grant_table->lock);
259 put_domain(rd);
260 return;
262 fail:
263 (void)__put_user(rc, &uop->handle);
264 spin_unlock(&rd->grant_table->lock);
265 put_domain(rd);
266 put_maptrack_handle(ld->grant_table, handle);
267 }
269 static long
270 gnttab_map_grant_ref(
271 gnttab_map_grant_ref_t *uop, unsigned int count)
272 {
273 int i;
274 for ( i = 0; i < count; i++ )
275 __gnttab_map_grant_ref(&uop[i]);
276 return 0;
277 }
279 static void
280 __gnttab_unmap_grant_ref(
281 gnttab_unmap_grant_ref_t *uop)
282 {
283 domid_t dom;
284 grant_ref_t ref;
285 u16 handle;
286 struct domain *ld, *rd;
288 active_grant_entry_t *act;
289 grant_entry_t *sha;
290 grant_mapping_t *map;
291 s16 rc = 0;
292 unsigned long frame, virt;
294 ld = current->domain;
296 /* Bitwise-OR avoids short-circuiting which screws control flow. */
297 if ( unlikely(__get_user(virt, &uop->host_virt_addr) |
298 __get_user(frame, &uop->dev_bus_addr) |
299 __get_user(handle, &uop->handle)) )
300 {
301 DPRINTK("Fault while reading gnttab_unmap_grant_ref_t.\n");
302 return; /* don't set status */
303 }
305 map = &ld->grant_table->maptrack[handle];
307 if ( unlikely(handle >= NR_MAPTRACK_ENTRIES) ||
308 unlikely(!(map->ref_and_flags & MAPTRACK_GNTMAP_MASK)) )
309 {
310 DPRINTK("Bad handle (%d).\n", handle);
311 (void)__put_user(GNTST_bad_handle, &uop->status);
312 return;
313 }
315 dom = map->domid;
316 ref = map->ref_and_flags >> MAPTRACK_REF_SHIFT;
318 if ( unlikely((rd = find_domain_by_id(dom)) == NULL) ||
319 unlikely(ld == rd) )
320 {
321 if ( rd != NULL )
322 put_domain(rd);
323 DPRINTK("Could not find domain %d\n", dom);
324 (void)__put_user(GNTST_bad_domain, &uop->status);
325 return;
326 }
328 act = &rd->grant_table->active[ref];
329 sha = &rd->grant_table->shared[ref];
331 spin_lock(&rd->grant_table->lock);
333 if ( frame != 0 )
334 {
335 if ( unlikely(frame != act->frame) )
336 PIN_FAIL(GNTST_general_error,
337 "Bad frame number doesn't match gntref.\n");
338 if ( map->ref_and_flags & GNTMAP_device_map )
339 act->pin -= (map->ref_and_flags & GNTMAP_readonly) ?
340 GNTPIN_devr_inc : GNTPIN_devw_inc;
341 }
342 else
343 {
344 frame = act->frame;
345 }
347 if ( (virt != 0) && (map->ref_and_flags & GNTMAP_host_map) )
348 {
349 act->pin -= (map->ref_and_flags & GNTMAP_readonly) ?
350 GNTPIN_hstr_inc : GNTPIN_hstw_inc;
351 }
353 if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) &&
354 !(map->ref_and_flags & GNTMAP_readonly) )
355 {
356 put_page_type(&frame_table[frame]);
357 clear_bit(_GTF_writing, &sha->flags);
358 }
360 if ( act->pin == 0 )
361 {
362 put_page(&frame_table[frame]);
363 clear_bit(_GTF_reading, &sha->flags);
364 }
366 fail:
367 (void)__put_user(rc, &uop->status);
368 spin_unlock(&rd->grant_table->lock);
369 put_domain(rd);
370 }
372 static long
373 gnttab_unmap_grant_ref(
374 gnttab_unmap_grant_ref_t *uop, unsigned int count)
375 {
376 int i;
377 for ( i = 0; i < count; i++ )
378 __gnttab_unmap_grant_ref(&uop[i]);
379 return 0;
380 }
382 static long
383 gnttab_setup_table(
384 gnttab_setup_table_t *uop, unsigned int count)
385 {
386 gnttab_setup_table_t op;
387 struct domain *d;
389 if ( count != 1 )
390 return -EINVAL;
392 if ( unlikely(copy_from_user(&op, uop, sizeof(op)) != 0) )
393 {
394 DPRINTK("Fault while reading gnttab_setup_table_t.\n");
395 return -EFAULT;
396 }
398 if ( unlikely(op.nr_frames > 1) )
399 {
400 DPRINTK("Xen only supports one grant-table frame per domain.\n");
401 (void)put_user(GNTST_general_error, &uop->status);
402 return 0;
403 }
405 if ( op.dom == DOMID_SELF )
406 {
407 op.dom = current->domain->id;
408 }
409 else if ( unlikely(!IS_PRIV(current->domain)) )
410 {
411 (void)put_user(GNTST_permission_denied, &uop->status);
412 return 0;
413 }
415 if ( unlikely((d = find_domain_by_id(op.dom)) == NULL) )
416 {
417 DPRINTK("Bad domid %d.\n", op.dom);
418 (void)put_user(GNTST_bad_domain, &uop->status);
419 return 0;
420 }
422 if ( op.nr_frames == 1 )
423 {
424 ASSERT(d->grant_table != NULL);
425 (void)put_user(GNTST_okay, &uop->status);
426 (void)put_user(virt_to_phys(d->grant_table) >> PAGE_SHIFT,
427 &uop->frame_list[0]);
428 }
430 put_domain(d);
431 return 0;
432 }
434 long
435 do_grant_table_op(
436 unsigned int cmd, void *uop, unsigned int count)
437 {
438 long rc;
440 /* XXX stubbed out XXX */
441 return -ENOSYS;
443 if ( count > 512 )
444 return -EINVAL;
446 LOCK_BIGLOCK(current->domain);
448 switch ( cmd )
449 {
450 case GNTTABOP_map_grant_ref:
451 if ( unlikely(!array_access_ok(
452 VERIFY_WRITE, uop, count, sizeof(gnttab_map_grant_ref_t))) )
453 return -EFAULT;
454 rc = gnttab_map_grant_ref((gnttab_map_grant_ref_t *)uop, count);
455 break;
456 case GNTTABOP_unmap_grant_ref:
457 if ( unlikely(!array_access_ok(
458 VERIFY_WRITE, uop, count, sizeof(gnttab_unmap_grant_ref_t))) )
459 return -EFAULT;
460 rc = gnttab_unmap_grant_ref((gnttab_unmap_grant_ref_t *)uop, count);
461 break;
462 case GNTTABOP_setup_table:
463 rc = gnttab_setup_table((gnttab_setup_table_t *)uop, count);
464 break;
465 default:
466 rc = -ENOSYS;
467 break;
468 }
470 UNLOCK_BIGLOCK(current->domain);
472 return rc;
473 }
475 int
476 gnttab_check_unmap(
477 struct domain *rd, struct domain *ld, unsigned long frame, int readonly)
478 {
479 return 0;
480 }
482 int
483 gnttab_prepare_for_transfer(
484 struct domain *rd, struct domain *ld, grant_ref_t ref)
485 {
486 grant_table_t *t;
487 grant_entry_t *e;
488 domid_t sdom;
489 u16 sflags;
490 u32 scombo, prev_scombo;
491 int retries = 0;
493 if ( unlikely((t = rd->grant_table) == NULL) ||
494 unlikely(ref >= NR_GRANT_ENTRIES) )
495 {
496 DPRINTK("Dom %d has no g.t., or ref is bad (%d).\n", rd->id, ref);
497 return 0;
498 }
500 spin_lock(&t->lock);
502 e = &t->shared[ref];
504 sflags = e->flags;
505 sdom = e->domid;
507 for ( ; ; )
508 {
509 if ( unlikely(sflags != GTF_accept_transfer) ||
510 unlikely(sdom != ld->id) )
511 {
512 DPRINTK("Bad flags (%x) or dom (%d). (NB. expected dom %d)\n",
513 sflags, sdom, ld->id);
514 goto fail;
515 }
517 /* Merge two 16-bit values into a 32-bit combined update. */
518 /* NB. Endianness! */
519 prev_scombo = scombo = ((u32)sdom << 16) | (u32)sflags;
521 /* NB. prev_scombo is updated in place to seen value. */
522 if ( unlikely(cmpxchg_user((u32 *)&e->flags, prev_scombo,
523 prev_scombo | GTF_transfer_committed)) )
524 {
525 DPRINTK("Fault while modifying shared flags and domid.\n");
526 goto fail;
527 }
529 /* Did the combined update work (did we see what we expected?). */
530 if ( likely(prev_scombo == scombo) )
531 break;
533 if ( retries++ == 4 )
534 {
535 DPRINTK("Shared grant entry is unstable.\n");
536 goto fail;
537 }
539 /* Didn't see what we expected. Split out the seen flags & dom. */
540 /* NB. Endianness! */
541 sflags = (u16)prev_scombo;
542 sdom = (u16)(prev_scombo >> 16);
543 }
545 spin_unlock(&t->lock);
546 return 1;
548 fail:
549 spin_unlock(&t->lock);
550 return 0;
551 }
553 void
554 gnttab_notify_transfer(
555 struct domain *rd, grant_ref_t ref, unsigned long frame)
556 {
557 wmb(); /* Ensure that the reassignment is globally visible. */
558 rd->grant_table->shared[ref].frame = frame;
559 }
561 int
562 grant_table_create(
563 struct domain *d)
564 {
565 grant_table_t *t;
566 int i;
568 if ( (t = xmalloc(grant_table_t)) == NULL )
569 goto no_mem;
571 /* Simple stuff. */
572 memset(t, 0, sizeof(*t));
573 spin_lock_init(&t->lock);
575 /* Active grant table. */
576 if ( (t->active = xmalloc_array(active_grant_entry_t, NR_GRANT_ENTRIES))
577 == NULL )
578 goto no_mem;
579 memset(t->active, 0, sizeof(active_grant_entry_t) * NR_GRANT_ENTRIES);
581 if ( (t->maptrack = (void *)alloc_xenheap_page()) == NULL )
582 goto no_mem;
583 memset(t->maptrack, 0, PAGE_SIZE);
584 for ( i = 0; i < NR_MAPTRACK_ENTRIES; i++ )
585 t->maptrack[i].ref_and_flags = (i+1) << MAPTRACK_REF_SHIFT;
587 /* Shared grant table. */
588 if ( (t->shared = (void *)alloc_xenheap_page()) == NULL )
589 goto no_mem;
590 memset(t->shared, 0, PAGE_SIZE);
591 SHARE_PFN_WITH_DOMAIN(virt_to_page(t->shared), d);
593 /* Okay, install the structure. */
594 wmb(); /* avoid races with lock-free access to d->grant_table */
595 d->grant_table = t;
596 return 0;
598 no_mem:
599 if ( t != NULL )
600 {
601 if ( t->active != NULL )
602 xfree(t->active);
603 if ( t->maptrack != NULL )
604 free_xenheap_page((unsigned long)t->maptrack);
605 xfree(t);
606 }
607 return -ENOMEM;
608 }
610 void
611 grant_table_destroy(
612 struct domain *d)
613 {
614 grant_table_t *t;
616 if ( (t = d->grant_table) != NULL )
617 {
618 /* Free memory relating to this grant table. */
619 d->grant_table = NULL;
620 free_xenheap_page((unsigned long)t->shared);
621 free_xenheap_page((unsigned long)t->maptrack);
622 xfree(t->active);
623 xfree(t);
624 }
625 }
627 void
628 grant_table_init(
629 void)
630 {
631 /* Nothing. */
632 }