1 /* $NetBSD: xen_shm_machdep.c,v 1.19 2026/10/04 06:09:56 mlelstv 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 #include <sys/cdefs.h> 28 __KERNEL_RCSID(0, "$NetBSD: xen_shm_machdep.c,v 1.19 2026/10/04 06:09:56 mlelstv Exp $"); 29 30 #include "opt_xen.h" 31 32 #include <sys/types.h> 33 #include <sys/kmem.h> 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/queue.h> 37 #include <sys/vmem.h> 38 #include <sys/kernel.h> 39 #include <uvm/uvm.h> 40 41 #include <machine/pmap.h> 42 #include <xen/hypervisor.h> 43 #include <xen/xen.h> 44 #include <xen/evtchn.h> 45 #include <xen/xenmem.h> 46 #include <xen/xen_shm.h> 47 48 /* 49 * Helper routines for the backend drivers. This implements the necessary 50 * functions to map a bunch of pages from foreign domains into our kernel VM 51 * space, do I/O to it, and unmap it. 52 */ 53 54 /* 55 * Map the memory referenced via grefp to supplied VA space. 56 * If there is a failure for particular gref, no memory is mapped 57 * and error is returned. 58 */ 59 int 60 xen_shm_map(int nentries, int domid, grant_ref_t *grefp, vaddr_t va, 61 grant_handle_t *handlep, int flags) 62 { 63 gnttab_map_grant_ref_t *op; 64 int ret, i; 65 #ifndef XENPV 66 paddr_t base_paddr; 67 #endif 68 69 #ifdef DIAGNOSTIC 70 if (nentries > XENSHM_MAX_PAGES_PER_REQUEST) { 71 panic("xen_shm_map: %d entries", nentries); 72 } 73 #endif 74 op = kmem_alloc(nentries * sizeof(*op), KM_NOSLEEP); 75 if (op == NULL) 76 return ENOMEM; 77 78 #ifndef XENPV 79 base_paddr = xenmem_alloc_pa(nentries * PAGE_SIZE, PAGE_SIZE, false); 80 if (base_paddr == 0) { 81 kmem_free(op, nentries * sizeof(*op)); 82 return ENOMEM; 83 } 84 #endif 85 86 for (i = 0; i < nentries; i++) { 87 #ifndef XENPV 88 op[i].host_addr = base_paddr + i * PAGE_SIZE; 89 #else 90 op[i].host_addr = va + i * PAGE_SIZE; 91 #endif 92 op[i].dom = domid; 93 op[i].ref = grefp[i]; 94 op[i].flags = GNTMAP_host_map | 95 ((flags & XSHM_RO) ? GNTMAP_readonly : 0); 96 } 97 98 ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, op, nentries); 99 if (__predict_false(ret < 0)) { 100 #ifdef DIAGNOSTIC 101 printf("%s: HYPERVISOR_grant_table_op failed %d\n", __func__, 102 ret); 103 #endif 104 ret = EINVAL; 105 goto err1; 106 } 107 108 /* 109 * If ret is positive, it means there was an error in processing, 110 * and only first ret entries were actually handled. If it's zero, 111 * it only means all entries were processed, but there could still 112 * be failure. 113 */ 114 if (__predict_false(ret > 0 && ret < nentries)) { 115 nentries = ret; 116 } 117 118 for (i = 0; i < nentries; i++) { 119 if (__predict_false(op[i].status)) { 120 #ifdef DIAGNOSTIC 121 printf("%s: op[%d] bad status %d gref %u\n", __func__, 122 i, op[i].status, grefp[i]); 123 #endif 124 ret = 1; 125 continue; 126 } 127 handlep[i] = op[i].handle; 128 } 129 130 if (__predict_false(ret > 0)) { 131 int uncnt = 0; 132 gnttab_unmap_grant_ref_t unop[XENSHM_MAX_PAGES_PER_REQUEST]; 133 134 /* 135 * When returning error, make sure the successfully mapped 136 * entries are unmapped before returning the error. 137 * xen_shm_unmap() can't be used, it assumes 138 * linear consecutive space. 139 */ 140 for (i = uncnt = 0; i < nentries; i++) { 141 if (op[i].status == 0) { 142 #ifndef XENPV 143 unop[uncnt].host_addr = 144 base_paddr + i * PAGE_SIZE; 145 #else 146 unop[uncnt].host_addr = va + i * PAGE_SIZE; 147 #endif 148 unop[uncnt].dev_bus_addr = 0; 149 unop[uncnt].handle = handlep[i]; 150 uncnt++; 151 } 152 } 153 if (uncnt > 0) { 154 ret = HYPERVISOR_grant_table_op( 155 GNTTABOP_unmap_grant_ref, unop, uncnt); 156 if (ret != 0) { 157 panic("%s: unmap on error recovery failed" 158 " %d", __func__, ret); 159 } 160 } 161 #ifdef DIAGNOSTIC 162 printf("%s: HYPERVISOR_grant_table_op bad entry\n", 163 __func__); 164 #endif 165 ret = EINVAL; 166 goto err1; 167 } 168 #ifndef XENPV 169 for (i = 0; i < nentries; i++) { 170 pmap_kenter_pa(va + i * PAGE_SIZE, 171 base_paddr + i * PAGE_SIZE, 172 VM_PROT_READ | VM_PROT_WRITE, 0); 173 } 174 #endif 175 kmem_free(op, nentries * sizeof(*op)); 176 return 0; 177 err1: 178 #ifndef XENPV 179 xenmem_free_pa(base_paddr, nentries * PAGE_SIZE); 180 #endif 181 kmem_free(op, nentries * sizeof(*op)); 182 return ret; 183 } 184 185 void 186 xen_shm_unmap(vaddr_t va, int nentries, grant_handle_t *handlep) 187 { 188 gnttab_unmap_grant_ref_t op[XENSHM_MAX_PAGES_PER_REQUEST]; 189 int ret, i; 190 #ifndef XENPV 191 paddr_t base_paddr; 192 if (pmap_extract(pmap_kernel(), va, &base_paddr) != true) 193 panic("xen_shm_unmap: unmapped va"); 194 #endif 195 196 #ifdef DIAGNOSTIC 197 if (nentries > XENSHM_MAX_PAGES_PER_REQUEST) { 198 panic("xen_shm_unmap: %d entries", nentries); 199 } 200 #endif 201 202 for (i = 0; i < nentries; i++) { 203 #ifndef XENPV 204 pmap_kremove(va + i * PAGE_SIZE, PAGE_SIZE); 205 op[i].host_addr = base_paddr + i * PAGE_SIZE; 206 #else 207 op[i].host_addr = va + i * PAGE_SIZE; 208 #endif 209 op[i].dev_bus_addr = 0; 210 op[i].handle = handlep[i]; 211 } 212 213 ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, 214 op, nentries); 215 if (__predict_false(ret)) { 216 panic("xen_shm_unmap: unmap failed"); 217 } 218 #ifndef XENPV 219 xenmem_free_pa(base_paddr, PAGE_SIZE * nentries); 220 #endif 221 } 222