1/* 2 * Copyright (c) 2014 ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group. 8 */ 9 10#include <string.h> 11#include <barrelfish/barrelfish.h> 12 13#include <dma_internal.h> 14#include <dma/dma_mem_mgr.h> 15 16#include <debug.h> 17 18/* 19 * XXX: we are using a ordered linked list here instead of a proper tree structure 20 * assumption: the number of registered memory regions per binding is low 21 * and thus the lookup times acceptable low. 22 */ 23 24/** 25 * represents a tree of registered memory regions 26 */ 27struct dma_mem_tree 28{ 29 struct dma_mem_node *root; 30 size_t num_entries; 31}; 32 33/** 34 * represents a registered memory region 35 */ 36struct dma_mem_node 37{ 38 struct dma_mem_node *prev; ///< the previous region 39 struct dma_mem_node *next; ///< the next region 40 struct capref cap; ///< capability backing this region 41 lpaddr_t paddr; ///< converted physical address of the region 42 size_t size; ///< cached size in bytes of the region 43}; 44 45/** 46 * represents the state of a DMA memory region manager 47 */ 48struct dma_mem_mgr 49{ 50 struct dma_mem_tree regions; ///< registered memory regions 51 lpaddr_t range_min; ///< minimum allowed memory address 52 lpaddr_t range_max; ///< maximum allowed memory address 53 dma_mem_convert_fn convert; ///< converts the registered address to a dma usable 54 void *convert_arg; ///< argument for the convert function 55}; 56 57/* 58 * ---------------------------------------------------------------------------- 59 * Memory Region Management 60 * ---------------------------------------------------------------------------- 61 */ 62static errval_t mem_tree_init(struct dma_mem_tree *tree) 63{ 64 tree->root = NULL; 65 tree->num_entries = 0; 66 67 return SYS_ERR_OK; 68} 69 70/* 71 * returns the closest element to a given address. 72 * 73 * \returns dma_mem_node the previous element 74 * NULL if the closest would be before the first element 75 */ 76static struct dma_mem_node *mem_tree_find_nearest(struct dma_mem_tree *tree, 77 lpaddr_t base) 78{ 79 struct dma_mem_node *node = tree->root; 80 struct dma_mem_node *ret = tree->root; 81 assert(node); 82 while (node) { 83 if (node->paddr > base) { 84 return node->prev; 85 } 86 ret = node; 87 node = node->next; 88 } 89 assert(ret); 90 return ret; 91} 92 93static struct dma_mem_node *mem_tree_lookup(struct dma_mem_tree *tree, 94 lpaddr_t base, 95 size_t size) 96{ 97 if (tree->root == NULL) { 98 return NULL; 99 } 100 101 struct dma_mem_node *node = mem_tree_find_nearest(tree, base); 102 if (node == NULL) { 103 return NULL; 104 } 105 if (node->paddr <= base) { 106 /* the base may be within the region of this node. check size */ 107 if ((node->paddr + node->size) >= (base + size)) { 108 /* its within the registered region. Found!*/ 109 return node; 110 } 111 } 112 return NULL; 113} 114 115static errval_t mem_tree_insert(struct dma_mem_tree *tree, 116 struct dma_mem_node *entry) 117{ 118 if (tree->root == NULL) { 119 assert(tree->num_entries == 0); 120 entry->next = NULL; 121 entry->prev = NULL; 122 tree->root = entry; 123 tree->num_entries = 1; 124 return SYS_ERR_OK; 125 } 126 127 struct dma_mem_node *node = mem_tree_find_nearest(tree, entry->paddr); 128 129 if (node == NULL) { 130 /* insert at the beginning */ 131 entry->prev = NULL; 132 entry->next = tree->root; 133 tree->root->prev = entry; 134 tree->num_entries++; 135 return SYS_ERR_OK; 136 } 137 if (node->paddr <= entry->paddr) { 138 /* the base may be within the region of this node. check size */ 139 if ((node->paddr + node->size) >= (entry->paddr + entry->size)) { 140 /* this would lie within a already registered region */ 141 return DMA_ERR_MEM_OVERLAP; 142 } 143 } 144 145 if (node->next) { 146 node->next->prev = entry; 147 } 148 entry->prev = node; 149 entry->next = node->next; 150 node->next = entry; 151 tree->num_entries++; 152 153 return SYS_ERR_OK; 154} 155 156static struct dma_mem_node *mem_tree_remove(struct dma_mem_tree *tree, 157 lpaddr_t base) 158{ 159 if (tree->root == NULL) { 160 assert(tree->num_entries == 0); 161 return NULL; 162 } 163 struct dma_mem_node *node = mem_tree_lookup(tree, base, 0); 164 if (node == NULL) { 165 return NULL; 166 } 167 168 if (node->prev) { 169 node->prev->next = node->next; 170 } else { 171 tree->root = node->next; 172 } 173 174 if (node->next) { 175 node->next->prev = node->prev; 176 } 177 178 assert(tree->root || (tree->num_entries == 1)); 179 180 tree->num_entries--; 181 182 node->next = NULL; 183 node->prev = NULL; 184 185 return node; 186} 187 188/* 189 * ============================================================================ 190 * Public Interface 191 * ============================================================================ 192 */ 193 194/** 195 * \brief initializes the DMA memory region manager 196 * 197 * \param mem_mgr returned pointer to the mem manager structure 198 * \param range_min minimum allowed memory address 199 * \param range_max maximum allowed memory address 200 * 201 * \returns SYS_ERR_OK on success 202 * errval on failure 203 */ 204errval_t dma_mem_mgr_init(struct dma_mem_mgr **mem_mgr, 205 lpaddr_t range_min, 206 lpaddr_t range_max) 207{ 208 errval_t err; 209 210 struct dma_mem_mgr *mgr = calloc(1, sizeof(*mgr)); 211 if (mgr == NULL) { 212 return LIB_ERR_MALLOC_FAIL; 213 } 214 215 err = mem_tree_init(&mgr->regions); 216 if (err_is_fail(err)) { 217 free(mgr); 218 return err; 219 } 220 221 mgr->range_max = range_max; 222 mgr->range_min = range_min; 223 224 *mem_mgr = mgr; 225 226 return SYS_ERR_OK; 227} 228 229/** 230 * \brief sets the address conversion function to be used for translating the 231 * addresses 232 * 233 * \param mem_mgr DMA memory manager 234 * \param fn convert function to be called 235 * \param arg argument supplied for the convert function 236 */ 237void dma_mem_mgr_set_convert_fn(struct dma_mem_mgr *mem_mgr, 238 dma_mem_convert_fn fn, 239 void *arg) 240{ 241 mem_mgr->convert = fn; 242 mem_mgr->convert_arg = arg; 243} 244 245/** 246 * \brief registers a memory region to be used for DMA transfers 247 * 248 * \param mem_mgr DMA memory manager 249 * \param cap frame capability of the memory region to register 250 * 251 * \returns SYS_ERR_OK on success 252 * errval on failure 253 */ 254errval_t dma_mem_register(struct dma_mem_mgr *mem_mgr, 255 struct capref cap, genpaddr_t *retaddr) 256{ 257 errval_t err; 258 259 struct frame_identity frame_id; 260 err = frame_identify(cap, &frame_id); 261 if (err_is_fail(err)) { 262 return err; 263 } 264 265 DMAMEM_DEBUG("registering DMA memory range [0x%016lx, 0x%016lx]\n", 266 frame_id.base, frame_id.base + frame_id.bytes); 267 268 struct dma_mem_node *entry = calloc(1, sizeof(*entry)); 269 if (entry == NULL) { 270 return LIB_ERR_MALLOC_FAIL; 271 } 272 273 entry->cap = cap; 274 entry->paddr = frame_id.base; 275 entry->size = frame_id.bytes; 276 277 if (mem_mgr->convert) { 278 err = mem_mgr->convert(mem_mgr->convert_arg, cap, &entry->paddr, NULL); 279 DMAMEM_DEBUG("converted base address [0x%016lx] -> [0x%016lx]\n", 280 frame_id.base, entry->paddr); 281 if (err_is_fail(err)) { 282 return err; 283 } 284 } 285 286 if ((entry->paddr == 0) || (entry->paddr < mem_mgr->range_min) 287 || ((entry->paddr + entry->size) > mem_mgr->range_max)) { 288 free(entry); 289 return DMA_ERR_MEM_OUT_OF_RANGE; 290 } 291 292 err = mem_tree_insert(&mem_mgr->regions, entry); 293 if (err_is_fail(err)) { 294 free(entry); 295 return err; 296 } 297 298 if (retaddr) { 299 *retaddr = entry->paddr; 300 } 301 302 return SYS_ERR_OK; 303 304} 305 306/** 307 * \brief deregisters a memory region that it cannot longer be used for the 308 * memory manager 309 * 310 * \param mem_mgr DMA memory manager 311 * \param cap frame capability of the memory region to register 312 * 313 * \returns SYS_ERR_OK on success 314 * errval on failure 315 */ 316errval_t dma_mem_deregister(struct dma_mem_mgr *mem_mgr, 317 struct capref cap) 318{ 319 errval_t err; 320 321 struct frame_identity frame_id; 322 err = frame_identify(cap, &frame_id); 323 if (err_is_fail(err)) { 324 return err; 325 } 326 327 DMAMEM_DEBUG("deregister DMA memory range [0x%016lx, 0x%016lx]\n", 328 frame_id.base, frame_id.base + frame_id.bytes); 329 330 lpaddr_t addr = frame_id.base; 331 if (mem_mgr->convert) { 332 err = mem_mgr->convert(mem_mgr->convert_arg, cap, &addr, NULL); 333 DMAMEM_DEBUG("converted base address [0x%016lx] -> [0x%016lx]\n", 334 frame_id.base, addr); 335 if (err_is_fail(err)) { 336 return err; 337 } 338 } 339 340 if (addr == 0) { 341 return DMA_ERR_MEM_OUT_OF_RANGE; 342 } 343 344 struct dma_mem_node *entry = mem_tree_remove(&mem_mgr->regions, addr); 345 if (entry) { 346 free(entry); 347 return SYS_ERR_OK; 348 } 349 350 return DMA_ERR_MEM_NOT_REGISTERED; 351} 352 353/** 354 * \brief verifies if a addres-length pair lies completely within a 355 * registered memory region and translates the address 356 * 357 * \param mem_mgr DMA memory manager 358 * \param addr address to be looked up 359 * \param bytes length of the transfer in bytes 360 * \param dma_addr translated base address (if change in address space) 361 * 362 * \returns SYS_ERR_OK on success 363 * DMA_ERR_MEM_NOT_REGISTERED if the memory region is not registered 364 * DMA_ERR_OUT_OF_RANGE if the memory region is out of range 365 */ 366errval_t dma_mem_verify(struct dma_mem_mgr *mem_mgr, 367 lpaddr_t addr, 368 size_t bytes, 369 lpaddr_t *dma_addr) 370{ 371 lpaddr_t daddr = addr; 372# if 0 373 if (mem_mgr->convert) { 374 daddr = mem_mgr->convert(mem_mgr->convert_arg, addr, bytes); 375 DMAMEM_DEBUG("converted base address [0x%016lx] -> [0x%016lx]\n", addr, 376 daddr); 377 } 378#endif 379 380 DMAMEM_DEBUG("Verify DMA memory range [0x%016lx, 0x%016lx]\n", daddr, 381 daddr + bytes); 382 383 if ((daddr == 0) || (daddr < mem_mgr->range_min) 384 || ((daddr + bytes) > mem_mgr->range_max)) { 385 return DMA_ERR_MEM_OUT_OF_RANGE; 386 } 387 388 struct dma_mem_node *entry = mem_tree_lookup(&mem_mgr->regions, daddr, bytes); 389 if (entry) { 390 *dma_addr = daddr; 391 return SYS_ERR_OK; 392 } 393 394 return DMA_ERR_MEM_NOT_REGISTERED; 395} 396