1 /* $NetBSD: xengnt.c,v 1.14 2009/03/16 06:18:32 cegger Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Manuel Bouyer. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Manuel Bouyer. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: xengnt.c,v 1.14 2009/03/16 06:18:32 cegger Exp $"); 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/malloc.h> 40 #include <sys/queue.h> 41 #include <sys/extent.h> 42 #include <sys/kernel.h> 43 #include <uvm/uvm.h> 44 45 #include <xen/hypervisor.h> 46 #include <xen/xen.h> 47 #include <xen/granttables.h> 48 49 /* #define XENDEBUG */ 50 #ifdef XENDEBUG 51 #define DPRINTF(x) printf x 52 #else 53 #define DPRINTF(x) 54 #endif 55 56 #define NR_GRANT_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_t)) 57 58 /* Current number of frames making up the grant table */ 59 int gnt_nr_grant_frames; 60 /* Maximum number of frames that can make up the grant table */ 61 int gnt_max_grant_frames; 62 63 /* table of free grant entries */ 64 grant_ref_t *gnt_entries; 65 /* last free entry */ 66 int last_gnt_entry; 67 /* empty entry in the list */ 68 #define XENGNT_NO_ENTRY 0xffffffff 69 70 /* VM address of the grant table */ 71 grant_entry_t *grant_table; 72 73 static grant_ref_t xengnt_get_entry(void); 74 static void xengnt_free_entry(grant_ref_t); 75 static void xengnt_resume(void); 76 static int xengnt_more_entries(void); 77 78 void 79 xengnt_init(void) 80 { 81 struct gnttab_query_size query; 82 int rc; 83 int nr_grant_entries; 84 int i; 85 86 query.dom = DOMID_SELF; 87 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1); 88 if ((rc < 0) || (query.status != GNTST_okay)) 89 gnt_max_grant_frames = 4; /* Legacy max number of frames */ 90 else 91 gnt_max_grant_frames = query.max_nr_frames; 92 gnt_nr_grant_frames = 0; 93 94 nr_grant_entries = 95 gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE; 96 97 grant_table = (void *)uvm_km_alloc(kernel_map, 98 gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY); 99 if (grant_table == NULL) 100 panic("xengnt_init() no VM space"); 101 gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t), 102 M_DEVBUF, M_NOWAIT); 103 if (gnt_entries == NULL) 104 panic("xengnt_init() no space for bitmask"); 105 for (i = 0; i <= nr_grant_entries; i++) 106 gnt_entries[i] = XENGNT_NO_ENTRY; 107 108 last_gnt_entry = 0; 109 xengnt_resume(); 110 111 } 112 113 /* 114 * Resume grant table state 115 */ 116 static void 117 xengnt_resume(void) 118 { 119 int previous_nr_grant_frames = gnt_nr_grant_frames; 120 gnt_nr_grant_frames = 0; 121 while (gnt_nr_grant_frames < previous_nr_grant_frames) { 122 if (xengnt_more_entries() != 0) 123 panic("xengnt_resume: can't restore grant frames"); 124 } 125 } 126 127 /* 128 * Add another page to the grant table 129 * Returns 0 on success, ENOMEM on failure 130 */ 131 static int 132 xengnt_more_entries(void) 133 { 134 gnttab_setup_table_t setup; 135 unsigned long *pages; 136 int nframes_new = gnt_nr_grant_frames + 1; 137 int i; 138 139 if (gnt_nr_grant_frames == gnt_max_grant_frames) 140 return ENOMEM; 141 142 pages = malloc(nframes_new * sizeof(long), M_DEVBUF, M_NOWAIT); 143 if (pages == NULL) 144 return ENOMEM; 145 146 setup.dom = DOMID_SELF; 147 setup.nr_frames = nframes_new; 148 xenguest_handle(setup.frame_list) = pages; 149 150 /* 151 * setup the grant table, made of nframes_new frames 152 * and return the list of their virtual addresses 153 * in 'pages' 154 */ 155 if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0) 156 panic("%s: setup table failed", __func__); 157 if (setup.status != GNTST_okay) { 158 aprint_error("%s: setup table returned %d\n", 159 __func__, setup.status); 160 free(pages, M_DEVBUF); 161 return ENOMEM; 162 } 163 164 DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n", 165 pages[gnt_nr_grant_frames], 166 (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE)); 167 168 /* 169 * map between grant_table addresses and the machine addresses of 170 * the grant table frames 171 */ 172 pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE, 173 pages[gnt_nr_grant_frames] << PAGE_SHIFT, VM_PROT_WRITE); 174 175 /* 176 * add the grant entries associated to the last grant table frame 177 * and mark them as free 178 */ 179 for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE; 180 i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE; 181 i++) { 182 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 183 gnt_entries[last_gnt_entry] = i; 184 last_gnt_entry++; 185 } 186 gnt_nr_grant_frames = nframes_new; 187 free(pages, M_DEVBUF); 188 return 0; 189 } 190 191 /* 192 * Returns a reference to the first free entry in grant table 193 */ 194 static grant_ref_t 195 xengnt_get_entry(void) 196 { 197 grant_ref_t entry; 198 int s = splvm(); 199 static struct timeval xengnt_nonmemtime; 200 static const struct timeval xengnt_nonmemintvl = {5,0}; 201 202 if (last_gnt_entry == 0) { 203 if (xengnt_more_entries()) { 204 splx(s); 205 if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl)) 206 printf("xengnt_get_entry: out of grant " 207 "table entries\n"); 208 return XENGNT_NO_ENTRY; 209 } 210 } 211 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 212 last_gnt_entry--; 213 entry = gnt_entries[last_gnt_entry]; 214 gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY; 215 splx(s); 216 KASSERT(entry != XENGNT_NO_ENTRY); 217 KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE); 218 return entry; 219 } 220 221 /* 222 * Mark the grant table entry as free 223 */ 224 static void 225 xengnt_free_entry(grant_ref_t entry) 226 { 227 int s = splvm(); 228 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 229 KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE); 230 gnt_entries[last_gnt_entry] = entry; 231 last_gnt_entry++; 232 splx(s); 233 } 234 235 int 236 xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp) 237 { 238 *entryp = xengnt_get_entry(); 239 if (__predict_false(*entryp == XENGNT_NO_ENTRY)) 240 return ENOMEM; 241 242 grant_table[*entryp].frame = ma >> PAGE_SHIFT; 243 grant_table[*entryp].domid = dom; 244 /* 245 * ensure that the above values reach global visibility 246 * before permitting frame's access (done when we set flags) 247 */ 248 xen_rmb(); 249 grant_table[*entryp].flags = 250 GTF_permit_access | (ro ? GTF_readonly : 0); 251 return 0; 252 } 253 254 void 255 xengnt_revoke_access(grant_ref_t entry) 256 { 257 uint16_t flags, nflags; 258 259 nflags = grant_table[entry].flags; 260 261 do { 262 if ((flags = nflags) & (GTF_reading|GTF_writing)) 263 panic("xengnt_revoke_access: still in use"); 264 nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags, 265 flags, 0); 266 } while (nflags != flags); 267 xengnt_free_entry(entry); 268 } 269 270 int 271 xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp) 272 { 273 *entryp = xengnt_get_entry(); 274 if (__predict_false(*entryp == XENGNT_NO_ENTRY)) 275 return ENOMEM; 276 277 grant_table[*entryp].frame = 0; 278 grant_table[*entryp].domid = dom; 279 /* 280 * ensure that the above values reach global visibility 281 * before permitting frame's transfer (done when we set flags) 282 */ 283 xen_rmb(); 284 grant_table[*entryp].flags = GTF_accept_transfer; 285 return 0; 286 } 287 288 paddr_t 289 xengnt_revoke_transfer(grant_ref_t entry) 290 { 291 paddr_t page; 292 uint16_t flags; 293 294 /* if the transfer has not started, free the entry and return 0 */ 295 while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) { 296 if (xen_atomic_cmpxchg16(&grant_table[entry].flags, 297 flags, 0) == flags ) { 298 xengnt_free_entry(entry); 299 return 0; 300 } 301 HYPERVISOR_yield(); 302 } 303 304 /* If transfer in progress, wait for completion */ 305 while (!((flags = grant_table[entry].flags) & GTF_transfer_completed)) 306 HYPERVISOR_yield(); 307 308 /* Read the frame number /after/ reading completion status. */ 309 __insn_barrier(); 310 page = grant_table[entry].frame; 311 if (page == 0) 312 printf("xengnt_revoke_transfer: guest sent pa 0\n"); 313 314 xengnt_free_entry(entry); 315 return page; 316 } 317 318 int 319 xengnt_status(grant_ref_t entry) 320 { 321 return (grant_table[entry].flags & (GTF_reading|GTF_writing)); 322 } 323