1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * gnttab.c 31 * 32 * Granting foreign access to our memory reservation. 33 * 34 * Copyright (c) 2005, Christopher Clark 35 * Copyright (c) 2004-2005, K A Fraser 36 * 37 * This file may be distributed separately from the Linux kernel, or 38 * incorporated into other software packages, subject to the following license: 39 * 40 * Permission is hereby granted, free of charge, to any person obtaining a copy 41 * of this source file (the "Software"), to deal in the Software without 42 * restriction, including without limitation the rights to use, copy, modify, 43 * merge, publish, distribute, sublicense, and/or sell copies of the Software, 44 * and to permit persons to whom the Software is furnished to do so, subject to 45 * the following conditions: 46 * 47 * The above copyright notice and this permission notice shall be included in 48 * all copies or substantial portions of the Software. 49 * 50 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 55 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 56 * IN THE SOFTWARE. 57 */ 58 59 #include <sys/types.h> 60 #include <sys/archsystm.h> 61 #include <sys/hypervisor.h> 62 #include <sys/gnttab.h> 63 #include <sys/sysmacros.h> 64 #include <sys/machsystm.h> 65 #include <sys/systm.h> 66 #include <sys/mutex.h> 67 #include <sys/atomic.h> 68 #include <sys/spl.h> 69 #include <sys/condvar.h> 70 #include <sys/cpuvar.h> 71 #include <sys/taskq.h> 72 #include <sys/panic.h> 73 #include <sys/cmn_err.h> 74 #include <sys/promif.h> 75 #include <sys/cpu.h> 76 #include <sys/vmem.h> 77 #include <vm/hat_i86.h> 78 #include <sys/bootconf.h> 79 #include <sys/bootsvcs.h> 80 #include <sys/bootinfo.h> 81 #include <sys/multiboot.h> 82 #include <sys/bootvfs.h> 83 #include <sys/bootprops.h> 84 #include <vm/kboot_mmu.h> 85 #include <vm/seg_kmem.h> 86 87 #define cmpxchg(t, c, n) atomic_cas_16((t), (c), (n)) 88 89 /* External tools reserve first few grant table entries. */ 90 #define NR_RESERVED_ENTRIES 8 91 92 #define NR_GRANT_ENTRIES (NR_GRANT_FRAMES * \ 93 MMU_PAGESIZE / sizeof (grant_entry_t)) 94 #define GNTTAB_LIST_END (NR_GRANT_ENTRIES + 1) 95 #define VALID_GRANT_REF(r) ((r) < NR_GRANT_ENTRIES) 96 97 static grant_ref_t gnttab_list[NR_GRANT_ENTRIES]; 98 static int gnttab_free_count; 99 static grant_ref_t gnttab_free_head; 100 static kmutex_t gnttab_list_lock; 101 102 static grant_entry_t *shared; 103 #define GT_PGADDR(i) ((uintptr_t)shared + ((i) << PAGESHIFT)) 104 105 static struct gnttab_free_callback *gnttab_free_callback_list = NULL; 106 107 static int 108 get_free_entries(int count) 109 { 110 int ref; 111 grant_ref_t head; 112 113 mutex_enter(&gnttab_list_lock); 114 if (gnttab_free_count < count) { 115 mutex_exit(&gnttab_list_lock); 116 return (-1); 117 } 118 ref = head = gnttab_free_head; 119 gnttab_free_count -= count; 120 while (count-- > 1) 121 head = gnttab_list[head]; 122 gnttab_free_head = gnttab_list[head]; 123 gnttab_list[head] = GNTTAB_LIST_END; 124 mutex_exit(&gnttab_list_lock); 125 return (ref); 126 } 127 128 #define get_free_entry() get_free_entries(1) 129 130 static void 131 do_free_callbacks(void) 132 { 133 struct gnttab_free_callback *callback, *next; 134 135 callback = gnttab_free_callback_list; 136 gnttab_free_callback_list = NULL; 137 138 while (callback != NULL) { 139 next = callback->next; 140 if (gnttab_free_count >= callback->count) { 141 callback->next = NULL; 142 callback->fn(callback->arg); 143 } else { 144 callback->next = gnttab_free_callback_list; 145 gnttab_free_callback_list = callback; 146 } 147 callback = next; 148 } 149 } 150 151 static void 152 check_free_callbacks(void) 153 { 154 if (gnttab_free_callback_list) 155 do_free_callbacks(); 156 } 157 158 static void 159 put_free_entry(grant_ref_t ref) 160 { 161 ASSERT(VALID_GRANT_REF(ref)); 162 163 mutex_enter(&gnttab_list_lock); 164 gnttab_list[ref] = gnttab_free_head; 165 gnttab_free_head = ref; 166 gnttab_free_count++; 167 check_free_callbacks(); 168 mutex_exit(&gnttab_list_lock); 169 } 170 171 /* 172 * Public grant-issuing interface functions 173 */ 174 175 int 176 gnttab_grant_foreign_access(domid_t domid, gnttab_frame_t frame, int readonly) 177 { 178 int ref; 179 180 if ((ref = get_free_entry()) == -1) 181 return (-1); 182 183 ASSERT(VALID_GRANT_REF(ref)); 184 185 shared[ref].frame = frame; 186 shared[ref].domid = domid; 187 membar_producer(); 188 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); 189 190 return (ref); 191 } 192 193 void 194 gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, 195 gnttab_frame_t frame, int readonly) 196 { 197 ASSERT(VALID_GRANT_REF(ref)); 198 199 shared[ref].frame = frame; 200 shared[ref].domid = domid; 201 membar_producer(); 202 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); 203 } 204 205 206 int 207 gnttab_query_foreign_access(grant_ref_t ref) 208 { 209 uint16_t nflags; 210 211 ASSERT(VALID_GRANT_REF(ref)); 212 213 nflags = shared[ref].flags; 214 215 return (nflags & (GTF_reading|GTF_writing)); 216 } 217 218 /* ARGSUSED */ 219 int 220 gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) 221 { 222 uint16_t flags, nflags; 223 224 ASSERT(VALID_GRANT_REF(ref)); 225 226 nflags = shared[ref].flags; 227 do { 228 if ((flags = nflags) & (GTF_reading|GTF_writing)) { 229 cmn_err(CE_WARN, "g.e. still in use!"); 230 return (0); 231 } 232 } while ((nflags = cmpxchg(&shared[ref].flags, flags, 0)) != flags); 233 234 return (1); 235 } 236 237 void 238 gnttab_end_foreign_access(grant_ref_t ref, int readonly, gnttab_frame_t page) 239 { 240 ASSERT(VALID_GRANT_REF(ref)); 241 242 if (gnttab_end_foreign_access_ref(ref, readonly)) { 243 put_free_entry(ref); 244 /* 245 * XXPV - we don't support freeing a page here 246 */ 247 if (page != 0) { 248 cmn_err(CE_WARN, 249 "gnttab_end_foreign_access_ref: using unsupported free_page interface"); 250 /* free_page(page); */ 251 } 252 } else { 253 /* 254 * XXX This needs to be fixed so that the ref and page are 255 * placed on a list to be freed up later. 256 */ 257 cmn_err(CE_WARN, "leaking g.e. and page still in use!"); 258 } 259 } 260 261 int 262 gnttab_grant_foreign_transfer(domid_t domid) 263 { 264 int ref; 265 266 if ((ref = get_free_entry()) == -1) 267 return (-1); 268 269 ASSERT(VALID_GRANT_REF(ref)); 270 271 shared[ref].frame = 0; 272 shared[ref].domid = domid; 273 membar_producer(); 274 shared[ref].flags = GTF_accept_transfer; 275 276 return (ref); 277 } 278 279 void 280 gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid) 281 { 282 ASSERT(VALID_GRANT_REF(ref)); 283 284 shared[ref].frame = 0; 285 shared[ref].domid = domid; 286 membar_producer(); 287 shared[ref].flags = GTF_accept_transfer; 288 } 289 290 gnttab_frame_t 291 gnttab_end_foreign_transfer_ref(grant_ref_t ref) 292 { 293 gnttab_frame_t frame; 294 uint16_t flags; 295 296 ASSERT(VALID_GRANT_REF(ref)); 297 298 /* 299 * If a transfer is not even yet started, try to reclaim the grant 300 * reference and return failure (== 0). 301 */ 302 while (!((flags = shared[ref].flags) & GTF_transfer_committed)) { 303 if (cmpxchg(&shared[ref].flags, flags, 0) == flags) 304 return (0); 305 (void) HYPERVISOR_yield(); 306 } 307 308 /* If a transfer is in progress then wait until it is completed. */ 309 while (!(flags & GTF_transfer_completed)) { 310 flags = shared[ref].flags; 311 (void) HYPERVISOR_yield(); 312 } 313 314 /* Read the frame number /after/ reading completion status. */ 315 membar_consumer(); 316 frame = shared[ref].frame; 317 ASSERT(frame != 0); 318 319 return (frame); 320 } 321 322 gnttab_frame_t 323 gnttab_end_foreign_transfer(grant_ref_t ref) 324 { 325 gnttab_frame_t frame; 326 327 ASSERT(VALID_GRANT_REF(ref)); 328 329 frame = gnttab_end_foreign_transfer_ref(ref); 330 put_free_entry(ref); 331 return (frame); 332 } 333 334 void 335 gnttab_free_grant_reference(grant_ref_t ref) 336 { 337 ASSERT(VALID_GRANT_REF(ref)); 338 339 put_free_entry(ref); 340 } 341 342 void 343 gnttab_free_grant_references(grant_ref_t head) 344 { 345 grant_ref_t ref; 346 int count = 1; 347 348 if (head == GNTTAB_LIST_END) 349 return; 350 mutex_enter(&gnttab_list_lock); 351 ref = head; 352 while (gnttab_list[ref] != GNTTAB_LIST_END) { 353 ref = gnttab_list[ref]; 354 count++; 355 } 356 gnttab_list[ref] = gnttab_free_head; 357 gnttab_free_head = head; 358 gnttab_free_count += count; 359 check_free_callbacks(); 360 mutex_exit(&gnttab_list_lock); 361 } 362 363 int 364 gnttab_alloc_grant_references(uint16_t count, grant_ref_t *head) 365 { 366 int h = get_free_entries(count); 367 368 if (h == -1) 369 return (-1); 370 371 *head = h; 372 373 return (0); 374 } 375 376 int 377 gnttab_claim_grant_reference(grant_ref_t *private_head) 378 { 379 grant_ref_t g = *private_head; 380 381 if (g == GNTTAB_LIST_END) 382 return (-1); 383 *private_head = gnttab_list[g]; 384 return (g); 385 } 386 387 void 388 gnttab_release_grant_reference(grant_ref_t *private_head, grant_ref_t release) 389 { 390 ASSERT(VALID_GRANT_REF(release)); 391 392 gnttab_list[release] = *private_head; 393 *private_head = release; 394 } 395 396 void 397 gnttab_request_free_callback(struct gnttab_free_callback *callback, 398 void (*fn)(void *), void *arg, uint16_t count) 399 { 400 mutex_enter(&gnttab_list_lock); 401 if (callback->next) 402 goto out; 403 callback->fn = fn; 404 callback->arg = arg; 405 callback->count = count; 406 callback->next = gnttab_free_callback_list; 407 gnttab_free_callback_list = callback; 408 check_free_callbacks(); 409 out: 410 mutex_exit(&gnttab_list_lock); 411 } 412 413 void 414 gnttab_init(void) 415 { 416 gnttab_setup_table_t set; 417 gnttab_frame_t frames[NR_GRANT_FRAMES]; 418 int i; 419 420 set.dom = DOMID_SELF; 421 set.nr_frames = NR_GRANT_FRAMES; 422 /*LINTED: constant in conditional context*/ 423 set_xen_guest_handle(set.frame_list, frames); 424 425 /* 426 * Take 4 pages of grant table space from the hypervisor and map it 427 */ 428 if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) || 429 (set.status != 0)) { 430 cmn_err(CE_PANIC, "Grant Table setup failed"); 431 } 432 433 shared = vmem_xalloc(heap_arena, NR_GRANT_FRAMES * MMU_PAGESIZE, 434 MMU_PAGESIZE, 0, 0, 0, 0, VM_SLEEP); 435 436 for (i = 0; i < NR_GRANT_FRAMES; i++) 437 kbm_map_ma(FRAME_TO_MA(frames[i]), GT_PGADDR(i), 0); 438 439 for (i = NR_RESERVED_ENTRIES; i < NR_GRANT_ENTRIES; i++) 440 gnttab_list[i] = i + 1; 441 gnttab_free_count = NR_GRANT_ENTRIES - NR_RESERVED_ENTRIES; 442 gnttab_free_head = NR_RESERVED_ENTRIES; 443 444 mutex_init(&gnttab_list_lock, NULL, MUTEX_DEFAULT, NULL); 445 } 446 447 void 448 gnttab_resume(void) 449 { 450 gnttab_setup_table_t set; 451 gnttab_frame_t frames[NR_GRANT_FRAMES]; 452 int i; 453 454 set.dom = DOMID_SELF; 455 set.nr_frames = NR_GRANT_FRAMES; 456 /*LINTED: constant in conditional context*/ 457 set_xen_guest_handle(set.frame_list, frames); 458 459 /* 460 * Take NR_GRANT_FRAMES pages of grant table space from the 461 * hypervisor and map it 462 */ 463 if ((HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &set, 1) != 0) || 464 (set.status != 0)) { 465 cmn_err(CE_PANIC, "Grant Table setup failed"); 466 } 467 468 for (i = 0; i < NR_GRANT_FRAMES; i++) { 469 (void) HYPERVISOR_update_va_mapping(GT_PGADDR(i), 470 FRAME_TO_MA(frames[i]) | PT_VALID | PT_WRITABLE, 471 UVMF_INVLPG | UVMF_ALL); 472 } 473 } 474 475 void 476 gnttab_suspend(void) 477 { 478 int i; 479 480 /* 481 * clear grant table mappings before suspending 482 */ 483 for (i = 0; i < NR_GRANT_FRAMES; i++) { 484 (void) HYPERVISOR_update_va_mapping(GT_PGADDR(i), 485 0, UVMF_INVLPG); 486 } 487 } 488 489 /* 490 * Local variables: 491 * c-file-style: "solaris" 492 * indent-tabs-mode: t 493 * c-indent-level: 8 494 * c-basic-offset: 8 495 * tab-width: 8 496 * End: 497 */ 498