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