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