1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2018, Microsoft Corporation. 3 * All Rights Reserved. 4 */ 5 6 #include <fcntl.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <sys/types.h> 10 #include <sys/mman.h> 11 12 #include <rte_eal.h> 13 #include <rte_tailq.h> 14 #include <rte_log.h> 15 #include <rte_malloc.h> 16 #include <rte_bus.h> 17 #include <rte_bus_vmbus.h> 18 19 #include "private.h" 20 21 static struct rte_tailq_elem vmbus_tailq = { 22 .name = "VMBUS_RESOURCE_LIST", 23 }; 24 EAL_REGISTER_TAILQ(vmbus_tailq) 25 26 struct mapped_vmbus_resource * 27 vmbus_uio_find_resource(const struct rte_vmbus_device *dev) 28 { 29 struct mapped_vmbus_resource *uio_res; 30 struct mapped_vmbus_res_list *uio_res_list = 31 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list); 32 33 if (dev == NULL) 34 return NULL; 35 36 TAILQ_FOREACH(uio_res, uio_res_list, next) { 37 if (rte_uuid_compare(uio_res->id, dev->device_id) == 0) 38 return uio_res; 39 } 40 return NULL; 41 } 42 43 static int 44 vmbus_uio_map_secondary(struct rte_vmbus_device *dev) 45 { 46 struct mapped_vmbus_resource *uio_res; 47 struct vmbus_channel *chan; 48 int fd, i; 49 50 uio_res = vmbus_uio_find_resource(dev); 51 if (!uio_res) { 52 VMBUS_LOG(ERR, "Cannot find resource for device"); 53 return -1; 54 } 55 56 /* open /dev/uioX */ 57 fd = open(uio_res->path, O_RDWR); 58 if (fd < 0) { 59 VMBUS_LOG(ERR, "Cannot open %s: %s", 60 uio_res->path, strerror(errno)); 61 return -1; 62 } 63 64 for (i = 0; i != uio_res->nb_maps; i++) { 65 void *mapaddr; 66 off_t offset = i * rte_mem_page_size(); 67 68 mapaddr = vmbus_map_resource(uio_res->maps[i].addr, 69 fd, offset, 70 uio_res->maps[i].size, 0); 71 72 if (mapaddr == uio_res->maps[i].addr) 73 continue; /* successful map */ 74 75 if (mapaddr == MAP_FAILED) 76 VMBUS_LOG(ERR, 77 "mmap resource %d in secondary failed", i); 78 else { 79 VMBUS_LOG(ERR, 80 "mmap resource %d address mismatch", i); 81 vmbus_unmap_resource(mapaddr, uio_res->maps[i].size); 82 } 83 84 close(fd); 85 return -1; 86 } 87 88 /* fd is not needed in secondary process, close it */ 89 close(fd); 90 91 dev->primary = uio_res->primary; 92 if (!dev->primary) { 93 VMBUS_LOG(ERR, "missing primary channel"); 94 return -1; 95 } 96 97 STAILQ_FOREACH(chan, &dev->primary->subchannel_list, next) { 98 if (vmbus_uio_map_secondary_subchan(dev, chan) != 0) { 99 VMBUS_LOG(ERR, "cannot map secondary subchan"); 100 return -1; 101 } 102 } 103 return 0; 104 } 105 106 static int 107 vmbus_uio_map_primary(struct rte_vmbus_device *dev) 108 { 109 int i, ret; 110 struct mapped_vmbus_resource *uio_res = NULL; 111 struct mapped_vmbus_res_list *uio_res_list = 112 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list); 113 114 /* allocate uio resource */ 115 ret = vmbus_uio_alloc_resource(dev, &uio_res); 116 if (ret) 117 return ret; 118 119 /* Map the resources */ 120 for (i = 0; i < VMBUS_MAX_RESOURCE; i++) { 121 /* stop at empty BAR */ 122 if (dev->resource[i].len == 0) 123 break; 124 125 ret = vmbus_uio_map_resource_by_index(dev, i, uio_res, 0); 126 if (ret) 127 goto error; 128 } 129 130 uio_res->nb_maps = i; 131 132 TAILQ_INSERT_TAIL(uio_res_list, uio_res, next); 133 134 return 0; 135 error: 136 while (--i >= 0) { 137 vmbus_unmap_resource(uio_res->maps[i].addr, 138 (size_t)uio_res->maps[i].size); 139 } 140 vmbus_uio_free_resource(dev, uio_res); 141 return -1; 142 } 143 144 /* map the VMBUS resource of a VMBUS device in virtual memory */ 145 int 146 vmbus_uio_map_resource(struct rte_vmbus_device *dev) 147 { 148 struct mapped_vmbus_resource *uio_res; 149 int ret; 150 151 /* TODO: handle rescind */ 152 dev->intr_handle.fd = -1; 153 dev->intr_handle.uio_cfg_fd = -1; 154 dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN; 155 156 /* secondary processes - use already recorded details */ 157 if (rte_eal_process_type() != RTE_PROC_PRIMARY) 158 ret = vmbus_uio_map_secondary(dev); 159 else 160 ret = vmbus_uio_map_primary(dev); 161 162 if (ret != 0) 163 return ret; 164 165 uio_res = vmbus_uio_find_resource(dev); 166 if (!uio_res) { 167 VMBUS_LOG(ERR, "can not find resources!"); 168 return -EIO; 169 } 170 171 if (uio_res->nb_maps <= HV_MON_PAGE_MAP) { 172 VMBUS_LOG(ERR, "VMBUS: only %u resources found!", 173 uio_res->nb_maps); 174 return -EINVAL; 175 } 176 177 dev->int_page = (uint32_t *)((char *)uio_res->maps[HV_INT_PAGE_MAP].addr 178 + (rte_mem_page_size() >> 1)); 179 dev->monitor_page = uio_res->maps[HV_MON_PAGE_MAP].addr; 180 return 0; 181 } 182 183 static void 184 vmbus_uio_unmap(struct mapped_vmbus_resource *uio_res) 185 { 186 int i; 187 188 if (uio_res == NULL) 189 return; 190 191 for (i = 0; i != uio_res->nb_maps; i++) { 192 vmbus_unmap_resource(uio_res->maps[i].addr, 193 (size_t)uio_res->maps[i].size); 194 } 195 } 196 197 /* unmap the VMBUS resource of a VMBUS device in virtual memory */ 198 void 199 vmbus_uio_unmap_resource(struct rte_vmbus_device *dev) 200 { 201 struct mapped_vmbus_resource *uio_res; 202 struct mapped_vmbus_res_list *uio_res_list = 203 RTE_TAILQ_CAST(vmbus_tailq.head, mapped_vmbus_res_list); 204 205 if (dev == NULL) 206 return; 207 208 /* find an entry for the device */ 209 uio_res = vmbus_uio_find_resource(dev); 210 if (uio_res == NULL) 211 return; 212 213 /* secondary processes - just free maps */ 214 if (rte_eal_process_type() != RTE_PROC_PRIMARY) 215 return vmbus_uio_unmap(uio_res); 216 217 TAILQ_REMOVE(uio_res_list, uio_res, next); 218 219 /* unmap all resources */ 220 vmbus_uio_unmap(uio_res); 221 222 /* free uio resource */ 223 rte_free(uio_res); 224 225 /* close fd if in primary process */ 226 close(dev->intr_handle.fd); 227 if (dev->intr_handle.uio_cfg_fd >= 0) { 228 close(dev->intr_handle.uio_cfg_fd); 229 dev->intr_handle.uio_cfg_fd = -1; 230 } 231 232 dev->intr_handle.fd = -1; 233 dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN; 234 } 235