debuggers.hg

view xen/common/grant_table.c @ 3726:88957a238191

bitkeeper revision 1.1159.1.544 (4207248crq3YxiyLWjUehtHv_Yd3tg)

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