blob: 301869f60dc7d7494c7c2b11dc4770251506ee8e [file] [log] [blame]
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -07001/******************************************************************************
2 * grant_table.c
3 *
4 * Granting foreign access to our memory reservation.
5 *
6 * Copyright (c) 2005-2006, Christopher Clark
7 * Copyright (c) 2004-2005, K A Fraser
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version 2
11 * as published by the Free Software Foundation; or, when distributed
12 * separately from the Linux kernel or incorporated into other
13 * software packages, subject to the following license:
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this source file (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use, copy, modify,
18 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
19 * and to permit persons to whom the Software is furnished to do so, subject to
20 * the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
31 * IN THE SOFTWARE.
32 */
33
34#include <linux/module.h>
35#include <linux/sched.h>
36#include <linux/mm.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090037#include <linux/slab.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070038#include <linux/vmalloc.h>
39#include <linux/uaccess.h>
Stefano Stabellini183d03c2010-05-17 17:08:21 +010040#include <linux/io.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070041
Jeremy Fitzhardinge1ccbf532009-10-06 15:11:14 -070042#include <xen/xen.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070043#include <xen/interface/xen.h>
44#include <xen/page.h>
45#include <xen/grant_table.h>
Stefano Stabellini183d03c2010-05-17 17:08:21 +010046#include <xen/interface/memory.h>
Annie Li85ff6ac2011-11-22 09:59:21 +080047#include <xen/hvc-console.h>
Jeremy Fitzhardingeecbf29c2008-12-16 12:37:07 -080048#include <asm/xen/hypercall.h>
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070049
50#include <asm/pgtable.h>
51#include <asm/sync_bitops.h>
52
53
54/* External tools reserve first few grant table entries. */
55#define NR_RESERVED_ENTRIES 8
56#define GNTTAB_LIST_END 0xffffffff
Annie Li85ff6ac2011-11-22 09:59:21 +080057#define GREFS_PER_GRANT_FRAME \
58(grant_table_version == 1 ? \
59(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \
60(PAGE_SIZE / sizeof(union grant_entry_v2)))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070061
62static grant_ref_t **gnttab_list;
63static unsigned int nr_grant_frames;
64static unsigned int boot_max_nr_grant_frames;
65static int gnttab_free_count;
66static grant_ref_t gnttab_free_head;
67static DEFINE_SPINLOCK(gnttab_list_lock);
Stefano Stabellini183d03c2010-05-17 17:08:21 +010068unsigned long xen_hvm_resume_frames;
69EXPORT_SYMBOL_GPL(xen_hvm_resume_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -070070
Annie Li0f9f5a92011-11-22 09:58:06 +080071static union {
72 struct grant_entry_v1 *v1;
Annie Li85ff6ac2011-11-22 09:59:21 +080073 union grant_entry_v2 *v2;
Annie Li0f9f5a92011-11-22 09:58:06 +080074 void *addr;
75} gnttab_shared;
76
77/*This is a structure of function pointers for grant table*/
78struct gnttab_ops {
79 /*
80 * Mapping a list of frames for storing grant entries. First input
81 * parameter is used to storing grant table address when grant table
82 * being setup, second parameter is the number of frames to map grant
83 * table. Returning GNTST_okay means success and negative value means
84 * failure.
85 */
86 int (*map_frames)(unsigned long *, unsigned int);
87 /*
88 * Release a list of frames which are mapped in map_frames for grant
89 * entry status.
90 */
91 void (*unmap_frames)(void);
92 /*
93 * Introducing a valid entry into the grant table, granting the frame
94 * of this grant entry to domain for accessing, or transfering, or
95 * transitively accessing. First input parameter is reference of this
96 * introduced grant entry, second one is domid of granted domain, third
97 * one is the frame to be granted, and the last one is status of the
98 * grant entry to be updated.
99 */
100 void (*update_entry)(grant_ref_t, domid_t, unsigned long, unsigned);
101 /*
102 * Stop granting a grant entry to domain for accessing. First input
103 * parameter is reference of a grant entry whose grant access will be
104 * stopped, second one is not in use now. If the grant entry is
105 * currently mapped for reading or writing, just return failure(==0)
106 * directly and don't tear down the grant access. Otherwise, stop grant
107 * access for this entry and return success(==1).
108 */
109 int (*end_foreign_access_ref)(grant_ref_t, int);
110 /*
111 * Stop granting a grant entry to domain for transfer. If tranfer has
112 * not started, just reclaim the grant entry and return failure(==0).
113 * Otherwise, wait for the transfer to complete and then return the
114 * frame.
115 */
116 unsigned long (*end_foreign_transfer_ref)(grant_ref_t);
117 /*
118 * Query the status of a grant entry. Input parameter is reference of
119 * queried grant entry, return value is the status of queried entry.
120 * Detailed status(writing/reading) can be gotten from the return value
121 * by bit operations.
122 */
123 int (*query_foreign_access)(grant_ref_t);
124};
125
126static struct gnttab_ops *gnttab_interface;
127
Annie Li85ff6ac2011-11-22 09:59:21 +0800128/*This reflects status of grant entries, so act as a global value*/
129static grant_status_t *grstatus;
130
Annie Li0f9f5a92011-11-22 09:58:06 +0800131static int grant_table_version;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700132
133static struct gnttab_free_callback *gnttab_free_callback_list;
134
135static int gnttab_expand(unsigned int req_entries);
136
137#define RPP (PAGE_SIZE / sizeof(grant_ref_t))
Annie Li85ff6ac2011-11-22 09:59:21 +0800138#define SPP (PAGE_SIZE / sizeof(grant_status_t))
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700139
140static inline grant_ref_t *__gnttab_entry(grant_ref_t entry)
141{
142 return &gnttab_list[(entry) / RPP][(entry) % RPP];
143}
144/* This can be used as an l-value */
145#define gnttab_entry(entry) (*__gnttab_entry(entry))
146
147static int get_free_entries(unsigned count)
148{
149 unsigned long flags;
Konrad Rzeszutek Wilk272800d2011-07-22 14:00:06 -0400150 int ref, rc = 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700151 grant_ref_t head;
152
153 spin_lock_irqsave(&gnttab_list_lock, flags);
154
155 if ((gnttab_free_count < count) &&
156 ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) {
157 spin_unlock_irqrestore(&gnttab_list_lock, flags);
158 return rc;
159 }
160
161 ref = head = gnttab_free_head;
162 gnttab_free_count -= count;
163 while (count-- > 1)
164 head = gnttab_entry(head);
165 gnttab_free_head = gnttab_entry(head);
166 gnttab_entry(head) = GNTTAB_LIST_END;
167
168 spin_unlock_irqrestore(&gnttab_list_lock, flags);
169
170 return ref;
171}
172
173static void do_free_callbacks(void)
174{
175 struct gnttab_free_callback *callback, *next;
176
177 callback = gnttab_free_callback_list;
178 gnttab_free_callback_list = NULL;
179
180 while (callback != NULL) {
181 next = callback->next;
182 if (gnttab_free_count >= callback->count) {
183 callback->next = NULL;
184 callback->fn(callback->arg);
185 } else {
186 callback->next = gnttab_free_callback_list;
187 gnttab_free_callback_list = callback;
188 }
189 callback = next;
190 }
191}
192
193static inline void check_free_callbacks(void)
194{
195 if (unlikely(gnttab_free_callback_list))
196 do_free_callbacks();
197}
198
199static void put_free_entry(grant_ref_t ref)
200{
201 unsigned long flags;
202 spin_lock_irqsave(&gnttab_list_lock, flags);
203 gnttab_entry(ref) = gnttab_free_head;
204 gnttab_free_head = ref;
205 gnttab_free_count++;
206 check_free_callbacks();
207 spin_unlock_irqrestore(&gnttab_list_lock, flags);
208}
209
Annie Li0f9f5a92011-11-22 09:58:06 +0800210/*
Annie Li85ff6ac2011-11-22 09:59:21 +0800211 * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2.
Annie Li0f9f5a92011-11-22 09:58:06 +0800212 * Introducing a valid entry into the grant table:
213 * 1. Write ent->domid.
214 * 2. Write ent->frame:
215 * GTF_permit_access: Frame to which access is permitted.
216 * GTF_accept_transfer: Pseudo-phys frame slot being filled by new
217 * frame, or zero if none.
218 * 3. Write memory barrier (WMB).
219 * 4. Write ent->flags, inc. valid type.
220 */
221static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid,
222 unsigned long frame, unsigned flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700223{
Annie Li0f9f5a92011-11-22 09:58:06 +0800224 gnttab_shared.v1[ref].domid = domid;
225 gnttab_shared.v1[ref].frame = frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700226 wmb();
Annie Li0f9f5a92011-11-22 09:58:06 +0800227 gnttab_shared.v1[ref].flags = flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700228}
229
Annie Li85ff6ac2011-11-22 09:59:21 +0800230static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid,
231 unsigned long frame, unsigned flags)
232{
233 gnttab_shared.v2[ref].hdr.domid = domid;
234 gnttab_shared.v2[ref].full_page.frame = frame;
235 wmb();
236 gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags;
237}
238
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700239/*
240 * Public grant-issuing interface functions
241 */
242void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
243 unsigned long frame, int readonly)
244{
Annie Li0f9f5a92011-11-22 09:58:06 +0800245 gnttab_interface->update_entry(ref, domid, frame,
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700246 GTF_permit_access | (readonly ? GTF_readonly : 0));
247}
248EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref);
249
250int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
251 int readonly)
252{
253 int ref;
254
255 ref = get_free_entries(1);
256 if (unlikely(ref < 0))
257 return -ENOSPC;
258
259 gnttab_grant_foreign_access_ref(ref, domid, frame, readonly);
260
261 return ref;
262}
263EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access);
264
Annie Li0f9f5a92011-11-22 09:58:06 +0800265static int gnttab_query_foreign_access_v1(grant_ref_t ref)
266{
267 return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing);
268}
269
Annie Li85ff6ac2011-11-22 09:59:21 +0800270static int gnttab_query_foreign_access_v2(grant_ref_t ref)
271{
272 return grstatus[ref] & (GTF_reading|GTF_writing);
273}
274
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700275int gnttab_query_foreign_access(grant_ref_t ref)
276{
Annie Li0f9f5a92011-11-22 09:58:06 +0800277 return gnttab_interface->query_foreign_access(ref);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700278}
279EXPORT_SYMBOL_GPL(gnttab_query_foreign_access);
280
Annie Li0f9f5a92011-11-22 09:58:06 +0800281static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700282{
283 u16 flags, nflags;
Annie Lib1e495b2011-11-22 09:58:47 +0800284 u16 *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700285
Annie Lib1e495b2011-11-22 09:58:47 +0800286 pflags = &gnttab_shared.v1[ref].flags;
287 nflags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700288 do {
289 flags = nflags;
290 if (flags & (GTF_reading|GTF_writing)) {
291 printk(KERN_ALERT "WARNING: g.e. still in use!\n");
292 return 0;
293 }
Annie Lib1e495b2011-11-22 09:58:47 +0800294 } while ((nflags = sync_cmpxchg(pflags, flags, 0)) != flags);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700295
296 return 1;
297}
Annie Li0f9f5a92011-11-22 09:58:06 +0800298
Annie Li85ff6ac2011-11-22 09:59:21 +0800299static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly)
300{
301 gnttab_shared.v2[ref].hdr.flags = 0;
302 mb();
303 if (grstatus[ref] & (GTF_reading|GTF_writing)) {
304 return 0;
305 } else {
306 /* The read of grstatus needs to have acquire
307 semantics. On x86, reads already have
308 that, and we just need to protect against
309 compiler reorderings. On other
310 architectures we may need a full
311 barrier. */
312#ifdef CONFIG_X86
313 barrier();
314#else
315 mb();
316#endif
317 }
318
319 return 1;
320}
321
Annie Li0f9f5a92011-11-22 09:58:06 +0800322int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly)
323{
324 return gnttab_interface->end_foreign_access_ref(ref, readonly);
325}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700326EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref);
327
328void gnttab_end_foreign_access(grant_ref_t ref, int readonly,
329 unsigned long page)
330{
331 if (gnttab_end_foreign_access_ref(ref, readonly)) {
332 put_free_entry(ref);
333 if (page != 0)
334 free_page(page);
335 } else {
336 /* XXX This needs to be fixed so that the ref and page are
337 placed on a list to be freed up later. */
338 printk(KERN_WARNING
339 "WARNING: leaking g.e. and page still in use!\n");
340 }
341}
342EXPORT_SYMBOL_GPL(gnttab_end_foreign_access);
343
344int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
345{
346 int ref;
347
348 ref = get_free_entries(1);
349 if (unlikely(ref < 0))
350 return -ENOSPC;
351 gnttab_grant_foreign_transfer_ref(ref, domid, pfn);
352
353 return ref;
354}
355EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer);
356
357void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
358 unsigned long pfn)
359{
Annie Li0f9f5a92011-11-22 09:58:06 +0800360 gnttab_interface->update_entry(ref, domid, pfn, GTF_accept_transfer);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700361}
362EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref);
363
Annie Li0f9f5a92011-11-22 09:58:06 +0800364static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700365{
366 unsigned long frame;
367 u16 flags;
Annie Lib1e495b2011-11-22 09:58:47 +0800368 u16 *pflags;
369
370 pflags = &gnttab_shared.v1[ref].flags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700371
372 /*
373 * If a transfer is not even yet started, try to reclaim the grant
374 * reference and return failure (== 0).
375 */
Annie Lib1e495b2011-11-22 09:58:47 +0800376 while (!((flags = *pflags) & GTF_transfer_committed)) {
377 if (sync_cmpxchg(pflags, flags, 0) == flags)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700378 return 0;
379 cpu_relax();
380 }
381
382 /* If a transfer is in progress then wait until it is completed. */
383 while (!(flags & GTF_transfer_completed)) {
Annie Lib1e495b2011-11-22 09:58:47 +0800384 flags = *pflags;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700385 cpu_relax();
386 }
387
388 rmb(); /* Read the frame number /after/ reading completion status. */
Annie Li0f9f5a92011-11-22 09:58:06 +0800389 frame = gnttab_shared.v1[ref].frame;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700390 BUG_ON(frame == 0);
391
392 return frame;
393}
Annie Li0f9f5a92011-11-22 09:58:06 +0800394
Annie Li85ff6ac2011-11-22 09:59:21 +0800395static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref)
396{
397 unsigned long frame;
398 u16 flags;
399 u16 *pflags;
400
401 pflags = &gnttab_shared.v2[ref].hdr.flags;
402
403 /*
404 * If a transfer is not even yet started, try to reclaim the grant
405 * reference and return failure (== 0).
406 */
407 while (!((flags = *pflags) & GTF_transfer_committed)) {
408 if (sync_cmpxchg(pflags, flags, 0) == flags)
409 return 0;
410 cpu_relax();
411 }
412
413 /* If a transfer is in progress then wait until it is completed. */
414 while (!(flags & GTF_transfer_completed)) {
415 flags = *pflags;
416 cpu_relax();
417 }
418
419 rmb(); /* Read the frame number /after/ reading completion status. */
420 frame = gnttab_shared.v2[ref].full_page.frame;
421 BUG_ON(frame == 0);
422
423 return frame;
424}
425
Annie Li0f9f5a92011-11-22 09:58:06 +0800426unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref)
427{
428 return gnttab_interface->end_foreign_transfer_ref(ref);
429}
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700430EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref);
431
432unsigned long gnttab_end_foreign_transfer(grant_ref_t ref)
433{
434 unsigned long frame = gnttab_end_foreign_transfer_ref(ref);
435 put_free_entry(ref);
436 return frame;
437}
438EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer);
439
440void gnttab_free_grant_reference(grant_ref_t ref)
441{
442 put_free_entry(ref);
443}
444EXPORT_SYMBOL_GPL(gnttab_free_grant_reference);
445
446void gnttab_free_grant_references(grant_ref_t head)
447{
448 grant_ref_t ref;
449 unsigned long flags;
450 int count = 1;
451 if (head == GNTTAB_LIST_END)
452 return;
453 spin_lock_irqsave(&gnttab_list_lock, flags);
454 ref = head;
455 while (gnttab_entry(ref) != GNTTAB_LIST_END) {
456 ref = gnttab_entry(ref);
457 count++;
458 }
459 gnttab_entry(ref) = gnttab_free_head;
460 gnttab_free_head = head;
461 gnttab_free_count += count;
462 check_free_callbacks();
463 spin_unlock_irqrestore(&gnttab_list_lock, flags);
464}
465EXPORT_SYMBOL_GPL(gnttab_free_grant_references);
466
467int gnttab_alloc_grant_references(u16 count, grant_ref_t *head)
468{
469 int h = get_free_entries(count);
470
471 if (h < 0)
472 return -ENOSPC;
473
474 *head = h;
475
476 return 0;
477}
478EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references);
479
480int gnttab_empty_grant_references(const grant_ref_t *private_head)
481{
482 return (*private_head == GNTTAB_LIST_END);
483}
484EXPORT_SYMBOL_GPL(gnttab_empty_grant_references);
485
486int gnttab_claim_grant_reference(grant_ref_t *private_head)
487{
488 grant_ref_t g = *private_head;
489 if (unlikely(g == GNTTAB_LIST_END))
490 return -ENOSPC;
491 *private_head = gnttab_entry(g);
492 return g;
493}
494EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference);
495
496void gnttab_release_grant_reference(grant_ref_t *private_head,
497 grant_ref_t release)
498{
499 gnttab_entry(release) = *private_head;
500 *private_head = release;
501}
502EXPORT_SYMBOL_GPL(gnttab_release_grant_reference);
503
504void gnttab_request_free_callback(struct gnttab_free_callback *callback,
505 void (*fn)(void *), void *arg, u16 count)
506{
507 unsigned long flags;
508 spin_lock_irqsave(&gnttab_list_lock, flags);
509 if (callback->next)
510 goto out;
511 callback->fn = fn;
512 callback->arg = arg;
513 callback->count = count;
514 callback->next = gnttab_free_callback_list;
515 gnttab_free_callback_list = callback;
516 check_free_callbacks();
517out:
518 spin_unlock_irqrestore(&gnttab_list_lock, flags);
519}
520EXPORT_SYMBOL_GPL(gnttab_request_free_callback);
521
522void gnttab_cancel_free_callback(struct gnttab_free_callback *callback)
523{
524 struct gnttab_free_callback **pcb;
525 unsigned long flags;
526
527 spin_lock_irqsave(&gnttab_list_lock, flags);
528 for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) {
529 if (*pcb == callback) {
530 *pcb = callback->next;
531 break;
532 }
533 }
534 spin_unlock_irqrestore(&gnttab_list_lock, flags);
535}
536EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback);
537
538static int grow_gnttab_list(unsigned int more_frames)
539{
540 unsigned int new_nr_grant_frames, extra_entries, i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700541 unsigned int nr_glist_frames, new_nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700542
543 new_nr_grant_frames = nr_grant_frames + more_frames;
544 extra_entries = more_frames * GREFS_PER_GRANT_FRAME;
545
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700546 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
547 new_nr_glist_frames =
548 (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
549 for (i = nr_glist_frames; i < new_nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700550 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC);
551 if (!gnttab_list[i])
552 goto grow_nomem;
553 }
554
555
556 for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames;
557 i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++)
558 gnttab_entry(i) = i + 1;
559
560 gnttab_entry(i) = gnttab_free_head;
561 gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames;
562 gnttab_free_count += extra_entries;
563
564 nr_grant_frames = new_nr_grant_frames;
565
566 check_free_callbacks();
567
568 return 0;
569
570grow_nomem:
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700571 for ( ; i >= nr_glist_frames; i--)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700572 free_page((unsigned long) gnttab_list[i]);
573 return -ENOMEM;
574}
575
576static unsigned int __max_nr_grant_frames(void)
577{
578 struct gnttab_query_size query;
579 int rc;
580
581 query.dom = DOMID_SELF;
582
583 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
584 if ((rc < 0) || (query.status != GNTST_okay))
585 return 4; /* Legacy max supported number of frames */
586
587 return query.max_nr_frames;
588}
589
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100590unsigned int gnttab_max_grant_frames(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700591{
592 unsigned int xen_max = __max_nr_grant_frames();
593
594 if (xen_max > boot_max_nr_grant_frames)
595 return boot_max_nr_grant_frames;
596 return xen_max;
597}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100598EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700599
Stefano Stabellini289b7772010-12-10 14:54:44 +0000600int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
Stefano Stabellini0930bba2011-09-29 11:57:56 +0100601 struct gnttab_map_grant_ref *kmap_ops,
602 struct page **pages, unsigned int count)
Stefano Stabellini289b7772010-12-10 14:54:44 +0000603{
604 int i, ret;
605 pte_t *pte;
606 unsigned long mfn;
607
608 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000609 if (ret)
610 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000611
Daniel De Graafaab8f112011-02-03 12:19:02 -0500612 if (xen_feature(XENFEAT_auto_translated_physmap))
613 return ret;
614
Stefano Stabellini289b7772010-12-10 14:54:44 +0000615 for (i = 0; i < count; i++) {
Ian Campbelldc4972a2011-03-04 17:38:21 +0000616 /* Do not add to override if the map failed. */
617 if (map_ops[i].status)
618 continue;
619
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500620 if (map_ops[i].flags & GNTMAP_contains_pte) {
621 pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
Stefano Stabellini289b7772010-12-10 14:54:44 +0000622 (map_ops[i].host_addr & ~PAGE_MASK));
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500623 mfn = pte_mfn(*pte);
624 } else {
625 /* If you really wanted to do this:
626 * mfn = PFN_DOWN(map_ops[i].dev_bus_addr);
627 *
628 * The reason we do not implement it is b/c on the
629 * unmap path (gnttab_unmap_refs) we have no means of
630 * checking whether the page is !GNTMAP_contains_pte.
631 *
632 * That is without some extra data-structure to carry
633 * the struct page, bool clear_pte, and list_head next
634 * tuples and deal with allocation/delallocation, etc.
635 *
636 * The users of this API set the GNTMAP_contains_pte
637 * flag so lets just return not supported until it
638 * becomes neccessary to implement.
639 */
640 return -EOPNOTSUPP;
641 }
Stefano Stabellini0930bba2011-09-29 11:57:56 +0100642 ret = m2p_add_override(mfn, pages[i], &kmap_ops[i]);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000643 if (ret)
644 return ret;
Stefano Stabellini289b7772010-12-10 14:54:44 +0000645 }
646
647 return ret;
648}
649EXPORT_SYMBOL_GPL(gnttab_map_refs);
650
651int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
652 struct page **pages, unsigned int count)
653{
654 int i, ret;
655
656 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000657 if (ret)
658 return ret;
659
Daniel De Graafaab8f112011-02-03 12:19:02 -0500660 if (xen_feature(XENFEAT_auto_translated_physmap))
661 return ret;
662
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000663 for (i = 0; i < count; i++) {
Konrad Rzeszutek Wilkcf8d9162011-02-28 17:58:48 -0500664 ret = m2p_remove_override(pages[i], true /* clear the PTE */);
Jeremy Fitzhardinge87f1d402010-12-13 14:42:30 +0000665 if (ret)
666 return ret;
667 }
Stefano Stabellini289b7772010-12-10 14:54:44 +0000668
669 return ret;
670}
671EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
672
Annie Li85ff6ac2011-11-22 09:59:21 +0800673static unsigned nr_status_frames(unsigned nr_grant_frames)
674{
675 return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP;
676}
677
Annie Li0f9f5a92011-11-22 09:58:06 +0800678static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes)
679{
680 int rc;
681
682 rc = arch_gnttab_map_shared(frames, nr_gframes,
683 gnttab_max_grant_frames(),
684 &gnttab_shared.addr);
685 BUG_ON(rc);
686
687 return 0;
688}
689
690static void gnttab_unmap_frames_v1(void)
691{
Annie Li85ff6ac2011-11-22 09:59:21 +0800692 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
693}
694
695static int gnttab_map_frames_v2(unsigned long *frames, unsigned int nr_gframes)
696{
697 uint64_t *sframes;
698 unsigned int nr_sframes;
699 struct gnttab_get_status_frames getframes;
700 int rc;
701
702 nr_sframes = nr_status_frames(nr_gframes);
703
704 /* No need for kzalloc as it is initialized in following hypercall
705 * GNTTABOP_get_status_frames.
706 */
707 sframes = kmalloc(nr_sframes * sizeof(uint64_t), GFP_ATOMIC);
708 if (!sframes)
709 return -ENOMEM;
710
711 getframes.dom = DOMID_SELF;
712 getframes.nr_frames = nr_sframes;
713 set_xen_guest_handle(getframes.frame_list, sframes);
714
715 rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
716 &getframes, 1);
717 if (rc == -ENOSYS) {
718 kfree(sframes);
719 return -ENOSYS;
720 }
721
722 BUG_ON(rc || getframes.status);
723
724 rc = arch_gnttab_map_status(sframes, nr_sframes,
725 nr_status_frames(gnttab_max_grant_frames()),
726 &grstatus);
727 BUG_ON(rc);
728 kfree(sframes);
729
730 rc = arch_gnttab_map_shared(frames, nr_gframes,
731 gnttab_max_grant_frames(),
732 &gnttab_shared.addr);
733 BUG_ON(rc);
734
735 return 0;
736}
737
738static void gnttab_unmap_frames_v2(void)
739{
740 arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames);
741 arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames));
Annie Li0f9f5a92011-11-22 09:58:06 +0800742}
743
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700744static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
745{
746 struct gnttab_setup_table setup;
747 unsigned long *frames;
748 unsigned int nr_gframes = end_idx + 1;
749 int rc;
750
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100751 if (xen_hvm_domain()) {
752 struct xen_add_to_physmap xatp;
753 unsigned int i = end_idx;
754 rc = 0;
755 /*
756 * Loop backwards, so that the first hypercall has the largest
757 * index, ensuring that the table will grow only once.
758 */
759 do {
760 xatp.domid = DOMID_SELF;
761 xatp.idx = i;
762 xatp.space = XENMAPSPACE_grant_table;
763 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
764 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
765 if (rc != 0) {
766 printk(KERN_WARNING
767 "grant table add_to_physmap failed, err=%d\n", rc);
768 break;
769 }
770 } while (i-- > start_idx);
771
772 return rc;
773 }
774
Annie Li85ff6ac2011-11-22 09:59:21 +0800775 /* No need for kzalloc as it is initialized in following hypercall
776 * GNTTABOP_setup_table.
777 */
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700778 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
779 if (!frames)
780 return -ENOMEM;
781
782 setup.dom = DOMID_SELF;
783 setup.nr_frames = nr_gframes;
Isaku Yamahata87e27cf2008-04-02 10:53:52 -0700784 set_xen_guest_handle(setup.frame_list, frames);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700785
786 rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1);
787 if (rc == -ENOSYS) {
788 kfree(frames);
789 return -ENOSYS;
790 }
791
792 BUG_ON(rc || setup.status);
793
Annie Li0f9f5a92011-11-22 09:58:06 +0800794 rc = gnttab_interface->map_frames(frames, nr_gframes);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700795
796 kfree(frames);
797
Annie Li0f9f5a92011-11-22 09:58:06 +0800798 return rc;
799}
800
801static struct gnttab_ops gnttab_v1_ops = {
802 .map_frames = gnttab_map_frames_v1,
803 .unmap_frames = gnttab_unmap_frames_v1,
804 .update_entry = gnttab_update_entry_v1,
805 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v1,
806 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v1,
807 .query_foreign_access = gnttab_query_foreign_access_v1,
808};
809
Annie Li85ff6ac2011-11-22 09:59:21 +0800810static struct gnttab_ops gnttab_v2_ops = {
811 .map_frames = gnttab_map_frames_v2,
812 .unmap_frames = gnttab_unmap_frames_v2,
813 .update_entry = gnttab_update_entry_v2,
814 .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2,
815 .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2,
816 .query_foreign_access = gnttab_query_foreign_access_v2,
817};
818
Annie Li0f9f5a92011-11-22 09:58:06 +0800819static void gnttab_request_version(void)
820{
Annie Li85ff6ac2011-11-22 09:59:21 +0800821 int rc;
822 struct gnttab_set_version gsv;
823
824 gsv.version = 2;
825 rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1);
826 if (rc == 0) {
827 grant_table_version = 2;
828 gnttab_interface = &gnttab_v2_ops;
829 } else if (grant_table_version == 2) {
830 /*
831 * If we've already used version 2 features,
832 * but then suddenly discover that they're not
833 * available (e.g. migrating to an older
834 * version of Xen), almost unbounded badness
835 * can happen.
836 */
837 panic("we need grant tables version 2, but only version 1 is available");
838 } else {
839 grant_table_version = 1;
840 gnttab_interface = &gnttab_v1_ops;
841 }
Annie Li0f9f5a92011-11-22 09:58:06 +0800842 printk(KERN_INFO "Grant tables using version %d layout.\n",
843 grant_table_version);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700844}
845
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100846int gnttab_resume(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700847{
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100848 unsigned int max_nr_gframes;
849
Annie Li0f9f5a92011-11-22 09:58:06 +0800850 gnttab_request_version();
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100851 max_nr_gframes = gnttab_max_grant_frames();
852 if (max_nr_gframes < nr_grant_frames)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700853 return -ENOSYS;
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100854
855 if (xen_pv_domain())
856 return gnttab_map(0, nr_grant_frames - 1);
857
Annie Li0f9f5a92011-11-22 09:58:06 +0800858 if (gnttab_shared.addr == NULL) {
859 gnttab_shared.addr = ioremap(xen_hvm_resume_frames,
860 PAGE_SIZE * max_nr_gframes);
861 if (gnttab_shared.addr == NULL) {
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100862 printk(KERN_WARNING
863 "Failed to ioremap gnttab share frames!");
864 return -ENOMEM;
865 }
866 }
867
868 gnttab_map(0, nr_grant_frames - 1);
869
870 return 0;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700871}
872
Jeremy Fitzhardinge0e913982008-05-26 23:31:27 +0100873int gnttab_suspend(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700874{
Annie Li0f9f5a92011-11-22 09:58:06 +0800875 gnttab_interface->unmap_frames();
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700876 return 0;
877}
878
879static int gnttab_expand(unsigned int req_entries)
880{
881 int rc;
882 unsigned int cur, extra;
883
884 cur = nr_grant_frames;
885 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
886 GREFS_PER_GRANT_FRAME);
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100887 if (cur + extra > gnttab_max_grant_frames())
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700888 return -ENOSPC;
889
890 rc = gnttab_map(cur, cur + extra - 1);
891 if (rc == 0)
892 rc = grow_gnttab_list(extra);
893
894 return rc;
895}
896
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100897int gnttab_init(void)
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700898{
899 int i;
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700900 unsigned int max_nr_glist_frames, nr_glist_frames;
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700901 unsigned int nr_init_grefs;
902
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700903 nr_grant_frames = 1;
904 boot_max_nr_grant_frames = __max_nr_grant_frames();
905
906 /* Determine the maximum number of frames required for the
907 * grant reference free list on the current hypervisor.
908 */
909 max_nr_glist_frames = (boot_max_nr_grant_frames *
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700910 GREFS_PER_GRANT_FRAME / RPP);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700911
912 gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *),
913 GFP_KERNEL);
914 if (gnttab_list == NULL)
915 return -ENOMEM;
916
Michael Abd-El-Malekbbc60c12008-04-04 02:33:48 -0700917 nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP;
918 for (i = 0; i < nr_glist_frames; i++) {
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700919 gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL);
920 if (gnttab_list[i] == NULL)
921 goto ini_nomem;
922 }
923
924 if (gnttab_resume() < 0)
925 return -ENODEV;
926
927 nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME;
928
929 for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++)
930 gnttab_entry(i) = i + 1;
931
932 gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END;
933 gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
934 gnttab_free_head = NR_RESERVED_ENTRIES;
935
936 printk("Grant table initialized\n");
937 return 0;
938
939 ini_nomem:
940 for (i--; i >= 0; i--)
941 free_page((unsigned long)gnttab_list[i]);
942 kfree(gnttab_list);
943 return -ENOMEM;
944}
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100945EXPORT_SYMBOL_GPL(gnttab_init);
Jeremy Fitzhardingead9a8612007-07-17 18:37:06 -0700946
Stefano Stabellini183d03c2010-05-17 17:08:21 +0100947static int __devinit __gnttab_init(void)
948{
949 /* Delay grant-table initialization in the PV on HVM case */
950 if (xen_hvm_domain())
951 return 0;
952
953 if (!xen_pv_domain())
954 return -ENODEV;
955
956 return gnttab_init();
957}
958
959core_initcall(__gnttab_init);