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