1*bad5336aSriastradh /* $NetBSD: xengnt.c,v 1.41 2023/02/25 00:35:52 riastradh Exp $ */
2f0dc72deSbouyer
3f0dc72deSbouyer /*
4f0dc72deSbouyer * Copyright (c) 2006 Manuel Bouyer.
5f0dc72deSbouyer *
6f0dc72deSbouyer * Redistribution and use in source and binary forms, with or without
7f0dc72deSbouyer * modification, are permitted provided that the following conditions
8f0dc72deSbouyer * are met:
9f0dc72deSbouyer * 1. Redistributions of source code must retain the above copyright
10f0dc72deSbouyer * notice, this list of conditions and the following disclaimer.
11f0dc72deSbouyer * 2. Redistributions in binary form must reproduce the above copyright
12f0dc72deSbouyer * notice, this list of conditions and the following disclaimer in the
13f0dc72deSbouyer * documentation and/or other materials provided with the distribution.
14f0dc72deSbouyer *
15f0dc72deSbouyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16f0dc72deSbouyer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17f0dc72deSbouyer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18f0dc72deSbouyer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19f0dc72deSbouyer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20f0dc72deSbouyer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21f0dc72deSbouyer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22f0dc72deSbouyer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23f0dc72deSbouyer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24f0dc72deSbouyer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25f0dc72deSbouyer *
26f0dc72deSbouyer */
27f0dc72deSbouyer
28a9cd1764Sbouyer #include <sys/cdefs.h>
29*bad5336aSriastradh __KERNEL_RCSID(0, "$NetBSD: xengnt.c,v 1.41 2023/02/25 00:35:52 riastradh Exp $");
30a9cd1764Sbouyer
31f0dc72deSbouyer #include <sys/types.h>
32f0dc72deSbouyer #include <sys/param.h>
33f0dc72deSbouyer #include <sys/systm.h>
343ec73fa9Sjdolecek #include <sys/kmem.h>
35f0dc72deSbouyer #include <sys/queue.h>
36f0dc72deSbouyer #include <sys/extent.h>
37f0dc72deSbouyer #include <sys/kernel.h>
38e8400248Scherry #include <sys/mutex.h>
39f0dc72deSbouyer #include <uvm/uvm.h>
40f0dc72deSbouyer
414e541343Sbouyer #include <xen/hypervisor.h>
424e541343Sbouyer #include <xen/xen.h>
434e541343Sbouyer #include <xen/granttables.h>
44f0dc72deSbouyer
45c0f95157Sbouyer #include "opt_xen.h"
46c0f95157Sbouyer
47d61110deSbouyer /* #define XENDEBUG */
48f0dc72deSbouyer #ifdef XENDEBUG
49f0dc72deSbouyer #define DPRINTF(x) printf x
50f0dc72deSbouyer #else
51f0dc72deSbouyer #define DPRINTF(x)
52f0dc72deSbouyer #endif
53f0dc72deSbouyer
54cbd5ff48Sroyger /* External tools reserve first few grant table entries. */
55cbd5ff48Sroyger #define NR_RESERVED_ENTRIES 8
56cbd5ff48Sroyger
573ecd3c34Sbouyer /* current supported version */
583ecd3c34Sbouyer int gnt_v = 0;
593ecd3c34Sbouyer #define GNT_ISV1 (gnt_v == 1)
603ecd3c34Sbouyer #define GNT_ISV2 (gnt_v == 2)
6164d2bae1Sjym /* Current number of frames making up the grant table */
62d61110deSbouyer int gnt_nr_grant_frames;
6364d2bae1Sjym /* Maximum number of frames that can make up the grant table */
64d61110deSbouyer int gnt_max_grant_frames;
65d61110deSbouyer
66d61110deSbouyer /* table of free grant entries */
67d61110deSbouyer grant_ref_t *gnt_entries;
6864d2bae1Sjym /* last free entry */
69d61110deSbouyer int last_gnt_entry;
7064d2bae1Sjym /* empty entry in the list */
7164d2bae1Sjym #define XENGNT_NO_ENTRY 0xffffffff
72f0dc72deSbouyer
73f0dc72deSbouyer /* VM address of the grant table */
743ecd3c34Sbouyer #define NR_GRANT_ENTRIES_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
753ecd3c34Sbouyer #define NR_GRANT_ENTRIES_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
763ecd3c34Sbouyer #define NR_GRANT_ENTRIES_PER_PAGE \
773ecd3c34Sbouyer ((gnt_v == 1) ? NR_GRANT_ENTRIES_PER_PAGE_V1 : NR_GRANT_ENTRIES_PER_PAGE_V2)
78c0f95157Sbouyer #define NR_GRANT_STATUS_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
79c0f95157Sbouyer
803ecd3c34Sbouyer union {
813ecd3c34Sbouyer grant_entry_v1_t *gntt_v1;
823ecd3c34Sbouyer grant_entry_v2_t *gntt_v2;
833ecd3c34Sbouyer void *gntt;
843ecd3c34Sbouyer } grant_table;
853ecd3c34Sbouyer
863ecd3c34Sbouyer /* Number of grant status frames (v2 only)*/
87c0f95157Sbouyer int gnt_status_frames;
88c0f95157Sbouyer
89e1b03486Sjdolecek grant_status_t *grant_status;
90e8400248Scherry kmutex_t grant_lock;
91f0dc72deSbouyer
92f0dc72deSbouyer static grant_ref_t xengnt_get_entry(void);
93f0dc72deSbouyer static void xengnt_free_entry(grant_ref_t);
94d61110deSbouyer static int xengnt_more_entries(void);
95e1b03486Sjdolecek static int xengnt_map_status(void);
963ecd3c34Sbouyer static bool xengnt_finish_init(void);
97f0dc72deSbouyer
98f0dc72deSbouyer void
xengnt_init(void)9996276685Scegger xengnt_init(void)
100f0dc72deSbouyer {
101d61110deSbouyer struct gnttab_query_size query;
102d61110deSbouyer int rc;
103d61110deSbouyer int nr_grant_entries;
104d61110deSbouyer int i;
105d61110deSbouyer
1063ecd3c34Sbouyer /* first try to see which version we support */
1073ecd3c34Sbouyer struct gnttab_set_version gntversion;
1083ecd3c34Sbouyer gnt_v = gntversion.version = 2;
1093ecd3c34Sbouyer rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gntversion, 1);
1103ecd3c34Sbouyer if (rc < 0 || gntversion.version != 2) {
1113ecd3c34Sbouyer aprint_debug("GNTTABOP_set_version 2 failed (%d), "
1123ecd3c34Sbouyer "fall back to version 1\n", rc);
1133ecd3c34Sbouyer gnt_v = 1;
1143ecd3c34Sbouyer }
1153ecd3c34Sbouyer
116d61110deSbouyer query.dom = DOMID_SELF;
117d61110deSbouyer rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1);
118d61110deSbouyer if ((rc < 0) || (query.status != GNTST_okay))
119d61110deSbouyer gnt_max_grant_frames = 4; /* Legacy max number of frames */
120d61110deSbouyer else
121d61110deSbouyer gnt_max_grant_frames = query.max_nr_frames;
122e1b03486Sjdolecek
123e1b03486Sjdolecek /*
124e1b03486Sjdolecek * Always allocate max number of grant frames, never expand in runtime
125e1b03486Sjdolecek */
126e1b03486Sjdolecek gnt_nr_grant_frames = gnt_max_grant_frames;
127e1b03486Sjdolecek
128d61110deSbouyer nr_grant_entries =
129d61110deSbouyer gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE;
130d61110deSbouyer
1313ecd3c34Sbouyer grant_table.gntt = (void *)uvm_km_alloc(kernel_map,
132d61110deSbouyer gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
1333ecd3c34Sbouyer if (grant_table.gntt == NULL)
134e1b03486Sjdolecek panic("xengnt_init() table no VM space");
135e1b03486Sjdolecek
1363ec73fa9Sjdolecek gnt_entries = kmem_alloc((nr_grant_entries + 1) * sizeof(grant_ref_t),
1373ec73fa9Sjdolecek KM_SLEEP);
138d61110deSbouyer for (i = 0; i <= nr_grant_entries; i++)
139d61110deSbouyer gnt_entries[i] = XENGNT_NO_ENTRY;
140f0dc72deSbouyer
1413ecd3c34Sbouyer if (GNT_ISV2) {
142e1b03486Sjdolecek gnt_status_frames =
143e1b03486Sjdolecek round_page(nr_grant_entries * sizeof(grant_status_t)) / PAGE_SIZE;
144e1b03486Sjdolecek grant_status = (void *)uvm_km_alloc(kernel_map,
145e1b03486Sjdolecek gnt_status_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY);
146e1b03486Sjdolecek if (grant_status == NULL)
147e1b03486Sjdolecek panic("xengnt_init() status no VM space");
1483ecd3c34Sbouyer }
149e1b03486Sjdolecek
150e8400248Scherry mutex_init(&grant_lock, MUTEX_DEFAULT, IPL_VM);
151e8400248Scherry
1523ecd3c34Sbouyer xengnt_finish_init();
153f0dc72deSbouyer }
154f0dc72deSbouyer
15564d2bae1Sjym /*
15664d2bae1Sjym * Resume grant table state
15764d2bae1Sjym */
158eba16022Sjym bool
xengnt_resume(void)15996276685Scegger xengnt_resume(void)
160f0dc72deSbouyer {
161a8ce07c2Sjdolecek int rc;
162eba16022Sjym
163a8ce07c2Sjdolecek struct gnttab_set_version gntversion;
1643ecd3c34Sbouyer KASSERT(gnt_v == 1 || gnt_v == 2);
1653ecd3c34Sbouyer gntversion.version = gnt_v;
166a8ce07c2Sjdolecek rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gntversion, 1);
1673ecd3c34Sbouyer
1683ecd3c34Sbouyer if (GNT_ISV2) {
1693ecd3c34Sbouyer if (rc < 0 || gntversion.version != 2) {
170a8ce07c2Sjdolecek panic("GNTTABOP_set_version 2 failed %d", rc);
1713ecd3c34Sbouyer }
1723ecd3c34Sbouyer } else {
1733ecd3c34Sbouyer if (rc == 0 && gntversion.version != 1) {
1743ecd3c34Sbouyer panic("GNTTABOP_set_version 1 failed");
1753ecd3c34Sbouyer }
1763ecd3c34Sbouyer }
1773ecd3c34Sbouyer
1783ecd3c34Sbouyer return xengnt_finish_init();
1793ecd3c34Sbouyer }
1803ecd3c34Sbouyer
1813ecd3c34Sbouyer static bool
xengnt_finish_init(void)1823ecd3c34Sbouyer xengnt_finish_init(void)
1833ecd3c34Sbouyer {
1843ecd3c34Sbouyer int previous_nr_grant_frames = gnt_nr_grant_frames;
185a8ce07c2Sjdolecek
186eba16022Sjym last_gnt_entry = 0;
187d61110deSbouyer gnt_nr_grant_frames = 0;
188eba16022Sjym
189d13f3410Sbouyer mutex_enter(&grant_lock);
190d61110deSbouyer while (gnt_nr_grant_frames < previous_nr_grant_frames) {
191d61110deSbouyer if (xengnt_more_entries() != 0)
192d61110deSbouyer panic("xengnt_resume: can't restore grant frames");
193d61110deSbouyer }
1943ecd3c34Sbouyer if (GNT_ISV2)
195e1b03486Sjdolecek xengnt_map_status();
196d13f3410Sbouyer mutex_exit(&grant_lock);
197eba16022Sjym return true;
198d61110deSbouyer }
199d61110deSbouyer
200e9dd42b1Sjym /*
201eba16022Sjym * Suspend grant table state
202eba16022Sjym */
203eba16022Sjym bool
xengnt_suspend(void)2046c09d8d6Sriz xengnt_suspend(void) {
205eba16022Sjym
206eba16022Sjym int i;
207eba16022Sjym
208d13f3410Sbouyer mutex_enter(&grant_lock);
209eba16022Sjym KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
210eba16022Sjym
211eba16022Sjym for (i = 0; i < last_gnt_entry; i++) {
212eba16022Sjym /* invalidate all grant entries (necessary for resume) */
213eba16022Sjym gnt_entries[i] = XENGNT_NO_ENTRY;
214eba16022Sjym }
215eba16022Sjym
216e1b03486Sjdolecek /* Remove virtual => machine mapping for grant table */
2173ecd3c34Sbouyer pmap_kremove((vaddr_t)grant_table.gntt, gnt_nr_grant_frames * PAGE_SIZE);
218e1b03486Sjdolecek
2193ecd3c34Sbouyer if (GNT_ISV2) {
220e1b03486Sjdolecek /* Remove virtual => machine mapping for status table */
221e1b03486Sjdolecek pmap_kremove((vaddr_t)grant_status, gnt_status_frames * PAGE_SIZE);
2223ecd3c34Sbouyer }
223e1b03486Sjdolecek
224eba16022Sjym pmap_update(pmap_kernel());
225d13f3410Sbouyer mutex_exit(&grant_lock);
226eba16022Sjym return true;
227eba16022Sjym }
228eba16022Sjym
229e1b03486Sjdolecek /*
230e1b03486Sjdolecek * Get status frames and enter them into the VA space.
231e1b03486Sjdolecek */
232e1b03486Sjdolecek static int
xengnt_map_status(void)233e1b03486Sjdolecek xengnt_map_status(void)
234e1b03486Sjdolecek {
2351fdedfbfSjdolecek uint64_t *pages;
236e1b03486Sjdolecek size_t sz;
237e1b03486Sjdolecek KASSERT(mutex_owned(&grant_lock));
2383ecd3c34Sbouyer KASSERT(GNT_ISV2);
239e1b03486Sjdolecek
2406c60eb71Sjdolecek sz = gnt_status_frames * sizeof(*pages);
241e1b03486Sjdolecek pages = kmem_alloc(sz, KM_NOSLEEP);
242e1b03486Sjdolecek if (pages == NULL)
243e1b03486Sjdolecek return ENOMEM;
244e1b03486Sjdolecek
245c24c993fSbouyer #ifdef XENPV
246c24c993fSbouyer gnttab_get_status_frames_t getstatus;
247c24c993fSbouyer int err;
248c24c993fSbouyer
249e1b03486Sjdolecek getstatus.dom = DOMID_SELF;
250e1b03486Sjdolecek getstatus.nr_frames = gnt_status_frames;
251e1b03486Sjdolecek set_xen_guest_handle(getstatus.frame_list, pages);
252e1b03486Sjdolecek
253e1b03486Sjdolecek /*
254e1b03486Sjdolecek * get the status frames, and return the list of their virtual
255e1b03486Sjdolecek * addresses in 'pages'
256e1b03486Sjdolecek */
257c0f95157Sbouyer if ((err = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames,
258c0f95157Sbouyer &getstatus, 1)) != 0)
259c0f95157Sbouyer panic("%s: get_status_frames failed: %d", __func__, err);
260e1b03486Sjdolecek if (getstatus.status != GNTST_okay) {
261e1b03486Sjdolecek aprint_error("%s: get_status_frames returned %d\n",
262e1b03486Sjdolecek __func__, getstatus.status);
263e1b03486Sjdolecek kmem_free(pages, sz);
264e1b03486Sjdolecek return ENOMEM;
265e1b03486Sjdolecek }
266c24c993fSbouyer #else /* XENPV */
267c24c993fSbouyer for (int i = 0; i < gnt_status_frames; i++) {
268c24c993fSbouyer struct vm_page *pg;
269c24c993fSbouyer struct xen_add_to_physmap xmap;
270e1b03486Sjdolecek
271c24c993fSbouyer pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
272c24c993fSbouyer pages[i] = atop(uvm_vm_page_to_phys(pg));
273c24c993fSbouyer
274c24c993fSbouyer xmap.domid = DOMID_SELF;
275c24c993fSbouyer xmap.space = XENMAPSPACE_grant_table;
276c24c993fSbouyer xmap.idx = i | XENMAPIDX_grant_table_status;
277c24c993fSbouyer xmap.gpfn = pages[i];
278c24c993fSbouyer
279c24c993fSbouyer if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xmap) < 0)
280c24c993fSbouyer panic("%s: Unable to add grant tables\n", __func__);
281c24c993fSbouyer }
282c24c993fSbouyer #endif /* XENPV */
283e1b03486Sjdolecek /*
284e1b03486Sjdolecek * map between status_table addresses and the machine addresses of
285e1b03486Sjdolecek * the status table frames
286e1b03486Sjdolecek */
287e1b03486Sjdolecek for (int i = 0; i < gnt_status_frames; i++) {
288e1b03486Sjdolecek pmap_kenter_ma(((vaddr_t)grant_status) + i * PAGE_SIZE,
289e1b03486Sjdolecek ((paddr_t)pages[i]) << PAGE_SHIFT,
290e1b03486Sjdolecek VM_PROT_WRITE, 0);
291e1b03486Sjdolecek }
292e1b03486Sjdolecek pmap_update(pmap_kernel());
293e1b03486Sjdolecek
294e1b03486Sjdolecek kmem_free(pages, sz);
295e1b03486Sjdolecek return 0;
296e1b03486Sjdolecek }
297eba16022Sjym
298eba16022Sjym /*
299e9dd42b1Sjym * Add another page to the grant table
300e9dd42b1Sjym * Returns 0 on success, ENOMEM on failure
301e9dd42b1Sjym */
302d61110deSbouyer static int
xengnt_more_entries(void)3035bed9654Scegger xengnt_more_entries(void)
304d61110deSbouyer {
305f0dc72deSbouyer gnttab_setup_table_t setup;
30651023c61Scegger u_long *pages;
307d61110deSbouyer int nframes_new = gnt_nr_grant_frames + 1;
308cbd5ff48Sroyger int i, start_gnt;
3093ec73fa9Sjdolecek size_t sz;
310d13f3410Sbouyer KASSERT(mutex_owned(&grant_lock));
311d61110deSbouyer
312d61110deSbouyer if (gnt_nr_grant_frames == gnt_max_grant_frames)
313d61110deSbouyer return ENOMEM;
314d61110deSbouyer
3156c60eb71Sjdolecek sz = nframes_new * sizeof(*pages);
316e1b03486Sjdolecek pages = kmem_alloc(sz, KM_NOSLEEP);
317d61110deSbouyer if (pages == NULL)
318d61110deSbouyer return ENOMEM;
319f0dc72deSbouyer
3207b6bc06aScherry if (xen_feature(XENFEAT_auto_translated_physmap)) {
3217b6bc06aScherry /*
3227b6bc06aScherry * Note: Although we allocate space for the entire
3237b6bc06aScherry * table, in this mode we only update one entry at a
3247b6bc06aScherry * time.
3257b6bc06aScherry */
3267b6bc06aScherry struct vm_page *pg;
3277b6bc06aScherry struct xen_add_to_physmap xmap;
3287b6bc06aScherry
3297b6bc06aScherry pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
3307b6bc06aScherry pages[gnt_nr_grant_frames] = atop(uvm_vm_page_to_phys(pg));
3317b6bc06aScherry
3327b6bc06aScherry xmap.domid = DOMID_SELF;
3337b6bc06aScherry xmap.space = XENMAPSPACE_grant_table;
3347b6bc06aScherry xmap.idx = gnt_nr_grant_frames;
3357b6bc06aScherry xmap.gpfn = pages[gnt_nr_grant_frames];
3367b6bc06aScherry
3377b6bc06aScherry if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xmap) < 0)
338c24c993fSbouyer panic("%s: Unable to add grant frames\n", __func__);
3397b6bc06aScherry
3407b6bc06aScherry } else {
341f0dc72deSbouyer setup.dom = DOMID_SELF;
342d61110deSbouyer setup.nr_frames = nframes_new;
3436f78ccc1Sjym set_xen_guest_handle(setup.frame_list, pages);
344f0dc72deSbouyer
345e9dd42b1Sjym /*
346e9dd42b1Sjym * setup the grant table, made of nframes_new frames
347e9dd42b1Sjym * and return the list of their virtual addresses
348e9dd42b1Sjym * in 'pages'
349e9dd42b1Sjym */
350f0dc72deSbouyer if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0)
351507459d7Sjym panic("%s: setup table failed", __func__);
352e9dd42b1Sjym if (setup.status != GNTST_okay) {
353507459d7Sjym aprint_error("%s: setup table returned %d\n",
354507459d7Sjym __func__, setup.status);
355e1b03486Sjdolecek kmem_free(pages, sz);
356d61110deSbouyer return ENOMEM;
357f0dc72deSbouyer }
3587b6bc06aScherry }
359d61110deSbouyer
360d61110deSbouyer DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n",
361d61110deSbouyer pages[gnt_nr_grant_frames],
362d61110deSbouyer (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE));
363d61110deSbouyer
364507459d7Sjym /*
365507459d7Sjym * map between grant_table addresses and the machine addresses of
366507459d7Sjym * the grant table frames
367507459d7Sjym */
3683ecd3c34Sbouyer pmap_kenter_ma(((vaddr_t)grant_table.gntt) + gnt_nr_grant_frames * PAGE_SIZE,
36951023c61Scegger ((paddr_t)pages[gnt_nr_grant_frames]) << PAGE_SHIFT,
37051023c61Scegger VM_PROT_WRITE, 0);
371155f2284Srmind pmap_update(pmap_kernel());
372d61110deSbouyer
373507459d7Sjym /*
374507459d7Sjym * add the grant entries associated to the last grant table frame
375cbd5ff48Sroyger * and mark them as free. Prevent using the first grants (from 0 to 8)
376cbd5ff48Sroyger * since they are used by the tools.
377507459d7Sjym */
378cbd5ff48Sroyger start_gnt = (gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE) <
379cbd5ff48Sroyger (NR_RESERVED_ENTRIES + 1) ?
380cbd5ff48Sroyger (NR_RESERVED_ENTRIES + 1) :
381cbd5ff48Sroyger (gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
382cbd5ff48Sroyger for (i = start_gnt;
383d61110deSbouyer i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE;
384d61110deSbouyer i++) {
385d61110deSbouyer KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
386d61110deSbouyer gnt_entries[last_gnt_entry] = i;
387d61110deSbouyer last_gnt_entry++;
388d61110deSbouyer }
389d61110deSbouyer gnt_nr_grant_frames = nframes_new;
390e1b03486Sjdolecek kmem_free(pages, sz);
391d61110deSbouyer return 0;
392f0dc72deSbouyer }
393f0dc72deSbouyer
394507459d7Sjym /*
395507459d7Sjym * Returns a reference to the first free entry in grant table
396507459d7Sjym */
397f0dc72deSbouyer static grant_ref_t
xengnt_get_entry(void)39896276685Scegger xengnt_get_entry(void)
399f0dc72deSbouyer {
400f0dc72deSbouyer grant_ref_t entry;
401d61110deSbouyer static struct timeval xengnt_nonmemtime;
4020f256b9dStron static const struct timeval xengnt_nonmemintvl = {5,0};
403f0dc72deSbouyer
4040bebe0a6Sjdolecek KASSERT(mutex_owned(&grant_lock));
4050bebe0a6Sjdolecek
406e1b03486Sjdolecek if (__predict_false(last_gnt_entry == 0)) {
407d61110deSbouyer if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl))
408d61110deSbouyer printf("xengnt_get_entry: out of grant "
409d61110deSbouyer "table entries\n");
410f0dc72deSbouyer return XENGNT_NO_ENTRY;
411f0dc72deSbouyer }
412d61110deSbouyer KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
413d61110deSbouyer last_gnt_entry--;
414d61110deSbouyer entry = gnt_entries[last_gnt_entry];
415d61110deSbouyer gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY;
416cbd5ff48Sroyger KASSERT(entry != XENGNT_NO_ENTRY && entry > NR_RESERVED_ENTRIES);
41731756f06Sjym KASSERT(last_gnt_entry >= 0);
41831756f06Sjym KASSERT(last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
419d61110deSbouyer return entry;
420d61110deSbouyer }
421f0dc72deSbouyer
422507459d7Sjym /*
423507459d7Sjym * Mark the grant table entry as free
424507459d7Sjym */
425f0dc72deSbouyer static void
xengnt_free_entry(grant_ref_t entry)426f0dc72deSbouyer xengnt_free_entry(grant_ref_t entry)
427f0dc72deSbouyer {
428e8400248Scherry mutex_enter(&grant_lock);
429cbd5ff48Sroyger KASSERT(entry > NR_RESERVED_ENTRIES);
430d61110deSbouyer KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY);
43131756f06Sjym KASSERT(last_gnt_entry >= 0);
43231756f06Sjym KASSERT(last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE);
433d61110deSbouyer gnt_entries[last_gnt_entry] = entry;
434d61110deSbouyer last_gnt_entry++;
435e8400248Scherry mutex_exit(&grant_lock);
436f0dc72deSbouyer }
437f0dc72deSbouyer
438f0dc72deSbouyer int
xengnt_grant_access(domid_t dom,paddr_t ma,int ro,grant_ref_t * entryp)439f0dc72deSbouyer xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp)
440f0dc72deSbouyer {
441e8400248Scherry mutex_enter(&grant_lock);
442d13f3410Sbouyer
443d13f3410Sbouyer *entryp = xengnt_get_entry();
444d13f3410Sbouyer if (__predict_false(*entryp == XENGNT_NO_ENTRY)) {
445d13f3410Sbouyer mutex_exit(&grant_lock);
446d13f3410Sbouyer return ENOMEM;
447d13f3410Sbouyer }
448d13f3410Sbouyer
4493ecd3c34Sbouyer if (GNT_ISV2) {
4503ecd3c34Sbouyer grant_table.gntt_v2[*entryp].full_page.frame = ma >> PAGE_SHIFT;
4513ecd3c34Sbouyer grant_table.gntt_v2[*entryp].hdr.domid = dom;
45264d2bae1Sjym /*
45364d2bae1Sjym * ensure that the above values reach global visibility
45464d2bae1Sjym * before permitting frame's access (done when we set flags)
45564d2bae1Sjym */
4563331f589Sriastradh xen_wmb();
4573ecd3c34Sbouyer grant_table.gntt_v2[*entryp].hdr.flags =
458f0dc72deSbouyer GTF_permit_access | (ro ? GTF_readonly : 0);
4593ecd3c34Sbouyer } else {
4603ecd3c34Sbouyer grant_table.gntt_v1[*entryp].frame = ma >> PAGE_SHIFT;
4613ecd3c34Sbouyer grant_table.gntt_v1[*entryp].domid = dom;
4623ecd3c34Sbouyer /*
4633ecd3c34Sbouyer * ensure that the above values reach global visibility
4643ecd3c34Sbouyer * before permitting frame's access (done when we set flags)
4653ecd3c34Sbouyer */
4663331f589Sriastradh xen_wmb();
4673ecd3c34Sbouyer grant_table.gntt_v1[*entryp].flags =
4683ecd3c34Sbouyer GTF_permit_access | (ro ? GTF_readonly : 0);
4693ecd3c34Sbouyer }
470e8400248Scherry mutex_exit(&grant_lock);
471f0dc72deSbouyer return 0;
472f0dc72deSbouyer }
473f0dc72deSbouyer
4743ecd3c34Sbouyer static inline uint16_t
xen_atomic_cmpxchg16(volatile uint16_t * ptr,uint16_t val,uint16_t newval)4753ecd3c34Sbouyer xen_atomic_cmpxchg16(volatile uint16_t *ptr, uint16_t val, uint16_t newval)
4763ecd3c34Sbouyer {
4773ecd3c34Sbouyer unsigned long result;
4783ecd3c34Sbouyer
4793ecd3c34Sbouyer __asm volatile(__LOCK_PREFIX
4803ecd3c34Sbouyer "cmpxchgw %w1,%2"
4813ecd3c34Sbouyer :"=a" (result)
4823ecd3c34Sbouyer :"q"(newval), "m" (*ptr), "0" (val)
4833ecd3c34Sbouyer :"memory");
4843ecd3c34Sbouyer
4853ecd3c34Sbouyer return result;
4863ecd3c34Sbouyer }
4873ecd3c34Sbouyer
488f0dc72deSbouyer void
xengnt_revoke_access(grant_ref_t entry)489f0dc72deSbouyer xengnt_revoke_access(grant_ref_t entry)
490f0dc72deSbouyer {
4913ecd3c34Sbouyer if (GNT_ISV2) {
4923ecd3c34Sbouyer grant_table.gntt_v2[entry].hdr.flags = 0;
493e1b03486Sjdolecek xen_mb(); /* Concurrent access by hypervisor */
494f0dc72deSbouyer
4953ecd3c34Sbouyer if (__predict_false(
4963ecd3c34Sbouyer (grant_status[entry] & (GTF_reading|GTF_writing)) != 0)) {
497e1b03486Sjdolecek printf("xengnt_revoke_access(%u): still in use\n",
498e1b03486Sjdolecek entry);
4993ecd3c34Sbouyer } else {
500f0dc72deSbouyer
501e1b03486Sjdolecek /*
5023ecd3c34Sbouyer * The read of grant_status needs to have acquire
5033ecd3c34Sbouyer * semantics.
504e1b03486Sjdolecek * Reads already have that on x86, so need only protect
505e1b03486Sjdolecek * against compiler reordering. May need full barrier
506e1b03486Sjdolecek * on other architectures.
507e1b03486Sjdolecek */
508e1b03486Sjdolecek __insn_barrier();
509e1b03486Sjdolecek }
5103ecd3c34Sbouyer } else {
5113ecd3c34Sbouyer uint16_t flags, nflags;
5123ecd3c34Sbouyer
5133ecd3c34Sbouyer nflags = grant_table.gntt_v1[entry].flags;
5143ecd3c34Sbouyer
5153ecd3c34Sbouyer do {
5163ecd3c34Sbouyer if ((flags = nflags) & (GTF_reading|GTF_writing))
5173ecd3c34Sbouyer panic("xengnt_revoke_access: still in use");
5183ecd3c34Sbouyer nflags = xen_atomic_cmpxchg16(
5193ecd3c34Sbouyer &grant_table.gntt_v1[entry].flags, flags, 0);
5203ecd3c34Sbouyer } while (nflags != flags);
5213ecd3c34Sbouyer
5223ecd3c34Sbouyer }
523f0dc72deSbouyer xengnt_free_entry(entry);
524f0dc72deSbouyer }
525f0dc72deSbouyer
526f0dc72deSbouyer int
xengnt_status(grant_ref_t entry)527f0dc72deSbouyer xengnt_status(grant_ref_t entry)
528f0dc72deSbouyer {
5293ecd3c34Sbouyer if (GNT_ISV2)
530e1b03486Sjdolecek return grant_status[entry] & (GTF_reading|GTF_writing);
5313ecd3c34Sbouyer else
5323ecd3c34Sbouyer return (grant_table.gntt_v1[entry].flags & (GTF_reading|GTF_writing));
533f0dc72deSbouyer }
534