1 /* 2 * DMA-related memory allocation utility functions. 3 * 4 * Copyright (C) 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/c/dataspace.h> 23 #include <l4/re/c/dma_space.h> 24 #include <l4/re/c/mem_alloc.h> 25 #include <l4/re/env.h> 26 #include <l4/re/protocols.h> 27 #include <l4/sys/factory.h> 28 #include <l4/sys/types.h> 29 #include <l4/vbus/vbus.h> 30 31 #include <ipc/cap_alloc.h> 32 #include <ipc/mem_ipc.h> 33 34 #include "dma.h" 35 #include "memory.h" 36 37 38 39 // Allocate a memory region of the given size for DMA. 40 41 long get_dma_region(unsigned long size, int align, struct dma_region *region) 42 { 43 // Memory allocation capabilities. 44 45 l4_cap_idx_t dma, vbus; 46 47 region->size = 0; 48 region->align = 0; 49 50 // Obtain capabilities for the DMA region and the vbus. 51 52 dma = ipc_cap_alloc(); 53 54 if (l4_is_invalid_cap(dma)) 55 return -L4_ENOENT; 56 57 vbus = l4re_env_get_cap("vbus"); 58 59 if (l4_is_invalid_cap(vbus)) 60 return -L4_ENOENT; 61 62 // Create the DMA space. 63 64 if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) 65 return -L4_ENOMEM; 66 67 // Find the DMA domain and assign the DMA space. 68 69 l4vbus_device_handle_t device = L4VBUS_NULL; 70 l4vbus_resource_t dma_resource; 71 72 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 73 return -L4_ENOENT; 74 75 if (l4vbus_assign_dma_domain(vbus, dma_resource.start, 76 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 77 dma)) 78 return -L4_ENOMEM; 79 80 // Allocate memory at the given alignment. 81 82 const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; 83 const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; 84 85 // Map the allocated memory, obtaining a virtual address. 86 87 region->vaddr = 0; 88 89 if (ipc_new_dataspace(size, alloc_flags, align, ®ion->mem)) 90 return -L4_ENOMEM; 91 92 if (ipc_attach_dataspace_align(region->mem, size, attach_flags, align, (void **) ®ion->vaddr)) 93 return -L4_ENOMEM; 94 95 // Obtain a physical address. 96 97 l4_size_t size_out = size; 98 region->paddr = 0; 99 100 if (l4re_dma_space_map(dma, region->mem | L4_CAP_FPAGE_RW, 0, &size_out, 0, 101 L4RE_DMA_SPACE_TO_DEVICE, ®ion->paddr)) 102 return -L4_ENOMEM; 103 104 // Test the mapped region size. 105 106 if (size_out != size) 107 return -L4_ENOMEM; 108 109 region->size = size_out; 110 region->align = align; 111 112 return L4_EOK; 113 }