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/c/rm.h> 26 #include <l4/re/env.h> 27 #include <l4/re/protocols.h> 28 #include <l4/sys/factory.h> 29 #include <l4/sys/types.h> 30 #include <l4/vbus/vbus.h> 31 32 #include <ipc/cap_alloc.h> 33 #include <ipc/mem_ipc.h> 34 35 #include "dma.h" 36 #include "memory.h" 37 38 39 40 // Obtain a DMA space to which memory regions may be associated. 41 42 long get_dma_space(l4_cap_idx_t *dma) 43 { 44 l4_cap_idx_t vbus = l4re_env_get_cap("vbus"); 45 46 if (l4_is_invalid_cap(vbus)) 47 return -L4_ENOENT; 48 49 // Obtain capabilities for the DMA region and the vbus. 50 51 *dma = ipc_cap_alloc(); 52 53 if (l4_is_invalid_cap(*dma)) 54 return -L4_ENOENT; 55 56 // Create the DMA space. 57 58 if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, 59 *dma))) 60 return -L4_ENOMEM; 61 62 // Find the DMA domain and assign the DMA space. 63 64 l4vbus_device_handle_t device = L4VBUS_NULL; 65 l4vbus_resource_t dma_resource; 66 67 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 68 return -L4_ENOENT; 69 70 if (l4vbus_assign_dma_domain(vbus, dma_resource.start, 71 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 72 *dma)) 73 return -L4_ENOMEM; 74 75 return L4_EOK; 76 } 77 78 // Obtain a memory region associated with the given DMA space. 79 80 long get_dma_region_for_space(unsigned long size, int align, l4_cap_idx_t dma, 81 struct dma_region *region) 82 { 83 // Allocate memory at the given alignment. 84 85 const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; 86 const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; 87 88 region->align = 0; 89 region->paddr = 0; 90 region->size = 0; 91 region->vaddr = 0; 92 93 // Map the allocated memory, obtaining a virtual address. 94 95 if (ipc_new_dataspace(size, alloc_flags, align, ®ion->mem)) 96 return -L4_ENOMEM; 97 98 if (ipc_attach_dataspace_align(region->mem, size, attach_flags, align, 99 (void **) ®ion->vaddr)) 100 return -L4_ENOMEM; 101 102 // Obtain a physical address. 103 104 l4_size_t size_out = size; 105 106 if (l4re_dma_space_map(dma, region->mem | L4_CAP_FPAGE_RW, 0, &size_out, 0, 107 L4RE_DMA_SPACE_TO_DEVICE, ®ion->paddr)) 108 return -L4_ENOMEM; 109 110 // Test the mapped region size. 111 112 if (size_out < size) 113 return -L4_ENOMEM; 114 115 region->align = align; 116 region->size = size_out; 117 118 return L4_EOK; 119 } 120 121 // Obtain a memory region associated with its own DMA space. 122 123 long get_dma_region(unsigned long size, int align, struct dma_region *region) 124 { 125 l4_cap_idx_t dma; 126 long err = get_dma_space(&dma); 127 128 if (err) 129 return err; 130 131 return get_dma_region_for_space(size, align, dma, region); 132 }