1 /* $NetBSD: xengnt.c,v 1.5 2007/11/22 16:17:10 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 * 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/types.h> 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/malloc.h> 37 #include <sys/queue.h> 38 #include <sys/extent.h> 39 #include <sys/kernel.h> 40 #include <uvm/uvm.h> 41 42 #include <xen/hypervisor.h> 43 #include <xen/xen.h> 44 #include <xen/granttables.h> 45 46 /* #define XENDEBUG */ 47 #ifdef XENDEBUG 48 #define DPRINTF(x) printf x 49 #else 50 #define DPRINTF(x) 51 #endif 52 53 #define NR_GRANT_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(grant_entry_t)) 54 55 int gnt_nr_grant_frames; 56 int gnt_max_grant_frames; 57 58 /* table of free grant entries */ 59 grant_ref_t *gnt_entries; 60 int last_gnt_entry; 61 62 /* VM address of the grant table */ 63 grant_entry_t *grant_table; 64 65 static grant_ref_t xengnt_get_entry(void); 66 #define XENGNT_NO_ENTRY 0xffffffff 67 static void xengnt_free_entry(grant_ref_t); 68 static void xengnt_resume(void); 69 static int xengnt_more_entries(void); 70 71 void 72 xengnt_init() 73 { 74 struct gnttab_query_size query; 75 int rc; 76 int nr_grant_entries; 77 int i; 78 79 query.dom = DOMID_SELF; 80 rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1); 81 if ((rc < 0) || (query.status != GNTST_okay)) 82 gnt_max_grant_frames = 4; /* Legacy max number of frames */ 83 else 84 gnt_max_grant_frames = query.max_nr_frames; 85 gnt_nr_grant_frames = 0; 86 87 nr_grant_entries = 88 gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE; 89 90 grant_table = (void *)uvm_km_alloc(kernel_map, 91 gnt_max_grant_frames * PAGE_SIZE, 0, UVM_KMF_VAONLY); 92 if (grant_table == NULL) 93 panic("xengnt_init() no VM space"); 94 gnt_entries = malloc((nr_grant_entries + 1) * sizeof(grant_ref_t), 95 M_DEVBUF, M_NOWAIT); 96 if (gnt_entries == NULL) 97 panic("xengnt_init() no space for bitmask"); 98 for (i = 0; i <= nr_grant_entries; i++) 99 gnt_entries[i] = XENGNT_NO_ENTRY; 100 101 last_gnt_entry = 0; 102 xengnt_resume(); 103 104 } 105 106 static void 107 xengnt_resume() 108 { 109 int previous_nr_grant_frames = gnt_nr_grant_frames; 110 gnt_nr_grant_frames = 0; 111 while (gnt_nr_grant_frames < previous_nr_grant_frames) { 112 if (xengnt_more_entries() != 0) 113 panic("xengnt_resume: can't restore grant frames"); 114 } 115 } 116 117 static int 118 xengnt_more_entries() 119 { 120 gnttab_setup_table_t setup; 121 unsigned long *pages; 122 int nframes_new = gnt_nr_grant_frames + 1; 123 int i; 124 125 if (gnt_nr_grant_frames == gnt_max_grant_frames) 126 return ENOMEM; 127 128 pages = malloc(nframes_new * sizeof(long), M_DEVBUF, M_NOWAIT); 129 if (pages == NULL) 130 return ENOMEM; 131 132 setup.dom = DOMID_SELF; 133 setup.nr_frames = nframes_new; 134 setup.frame_list = pages; 135 136 if (HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0) 137 panic("xengnt_more_entries: setup table failed"); 138 if (setup.status != 0) { 139 printf("xengnt_more_entries: setup table returned %d\n", 140 setup.status); 141 return ENOMEM; 142 } 143 144 DPRINTF(("xengnt_more_entries: map 0x%lx -> %p\n", 145 pages[gnt_nr_grant_frames], 146 (char *)grant_table + gnt_nr_grant_frames * PAGE_SIZE)); 147 148 pmap_kenter_ma(((vaddr_t)grant_table) + gnt_nr_grant_frames * PAGE_SIZE, 149 pages[gnt_nr_grant_frames] << PAGE_SHIFT, VM_PROT_WRITE); 150 151 152 for (i = gnt_nr_grant_frames * NR_GRANT_ENTRIES_PER_PAGE; 153 i < nframes_new * NR_GRANT_ENTRIES_PER_PAGE; 154 i++) { 155 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 156 gnt_entries[last_gnt_entry] = i; 157 last_gnt_entry++; 158 } 159 gnt_nr_grant_frames = nframes_new; 160 return 0; 161 } 162 163 static grant_ref_t 164 xengnt_get_entry() 165 { 166 grant_ref_t entry; 167 int s = splvm(); 168 static struct timeval xengnt_nonmemtime; 169 const static struct timeval xengnt_nonmemintvl = {5,0}; 170 171 if (last_gnt_entry == 0) { 172 if (xengnt_more_entries()) { 173 splx(s); 174 if (ratecheck(&xengnt_nonmemtime, &xengnt_nonmemintvl)) 175 printf("xengnt_get_entry: out of grant " 176 "table entries\n"); 177 return XENGNT_NO_ENTRY; 178 } 179 } 180 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 181 last_gnt_entry--; 182 entry = gnt_entries[last_gnt_entry]; 183 gnt_entries[last_gnt_entry] = XENGNT_NO_ENTRY; 184 splx(s); 185 KASSERT(entry != XENGNT_NO_ENTRY); 186 KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE); 187 return entry; 188 } 189 190 static void 191 xengnt_free_entry(grant_ref_t entry) 192 { 193 int s = splvm(); 194 KASSERT(gnt_entries[last_gnt_entry] == XENGNT_NO_ENTRY); 195 KASSERT(last_gnt_entry >= 0 && last_gnt_entry <= gnt_max_grant_frames * NR_GRANT_ENTRIES_PER_PAGE); 196 gnt_entries[last_gnt_entry] = entry; 197 last_gnt_entry++; 198 splx(s); 199 } 200 201 int 202 xengnt_grant_access(domid_t dom, paddr_t ma, int ro, grant_ref_t *entryp) 203 { 204 *entryp = xengnt_get_entry(); 205 if (__predict_false(*entryp == XENGNT_NO_ENTRY)) 206 return ENOMEM; 207 208 grant_table[*entryp].frame = ma >> PAGE_SHIFT; 209 grant_table[*entryp].domid = dom; 210 x86_lfence(); 211 grant_table[*entryp].flags = 212 GTF_permit_access | (ro ? GTF_readonly : 0); 213 return 0; 214 } 215 216 void 217 xengnt_revoke_access(grant_ref_t entry) 218 { 219 uint16_t flags, nflags; 220 221 nflags = grant_table[entry].flags; 222 223 do { 224 if ((flags = nflags) & (GTF_reading|GTF_writing)) 225 panic("xengnt_revoke_access: still in use"); 226 nflags = xen_atomic_cmpxchg16(&grant_table[entry].flags, 227 flags, 0); 228 } while (nflags != flags); 229 xengnt_free_entry(entry); 230 } 231 232 int 233 xengnt_grant_transfer(domid_t dom, grant_ref_t *entryp) 234 { 235 *entryp = xengnt_get_entry(); 236 if (__predict_false(*entryp == XENGNT_NO_ENTRY)) 237 return ENOMEM; 238 239 grant_table[*entryp].frame = 0; 240 grant_table[*entryp].domid =dom; 241 x86_lfence(); 242 grant_table[*entryp].flags = GTF_accept_transfer; 243 return 0; 244 } 245 246 paddr_t 247 xengnt_revoke_transfer(grant_ref_t entry) 248 { 249 paddr_t page; 250 uint16_t flags; 251 252 /* if the transfer has not started, free the entry and return 0 */ 253 while (!((flags = grant_table[entry].flags) & GTF_transfer_committed)) { 254 if (xen_atomic_cmpxchg16(&grant_table[entry].flags, 255 flags, 0) == flags ) { 256 xengnt_free_entry(entry); 257 return 0; 258 } 259 HYPERVISOR_yield(); 260 } 261 262 /* If transfer in progress, wait for completion */ 263 while (!((flags = grant_table[entry].flags) & GTF_transfer_completed)) 264 HYPERVISOR_yield(); 265 266 /* Read the frame number /after/ reading completion status. */ 267 __insn_barrier(); 268 page = grant_table[entry].frame; 269 if (page == 0) 270 printf("xengnt_revoke_transfer: guest sent pa 0\n"); 271 272 xengnt_free_entry(entry); 273 return page; 274 } 275 276 int 277 xengnt_status(grant_ref_t entry) 278 { 279 return (grant_table[entry].flags & (GTF_reading|GTF_writing)); 280 } 281