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