1 /* 2 * DMA-related memory allocation utility functions. 3 * 4 * Copyright (C) 2023 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, l4_addr_t *vaddr, 42 l4re_dma_space_dma_addr_t *paddr, l4_cap_idx_t *mem) 43 { 44 // Memory allocation capabilities. 45 46 l4_cap_idx_t dma, vbus; 47 48 // Obtain capabilities for the DMA region and the vbus. 49 50 dma = ipc_cap_alloc(); 51 52 if (l4_is_invalid_cap(dma)) 53 return -L4_ENOENT; 54 55 vbus = l4re_env_get_cap("vbus"); 56 57 if (l4_is_invalid_cap(vbus)) 58 return -L4_ENOENT; 59 60 // Create the DMA space. 61 62 if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma))) 63 return -L4_ENOMEM; 64 65 // Find the DMA domain and assign the DMA space. 66 67 l4vbus_device_handle_t device = L4VBUS_NULL; 68 l4vbus_resource_t dma_resource; 69 70 if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN)) 71 return -L4_ENOENT; 72 73 if (l4vbus_assign_dma_domain(vbus, dma_resource.start, 74 L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE, 75 dma)) 76 return -L4_ENOMEM; 77 78 // Allocate memory at the given alignment. 79 80 const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED; 81 const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW; 82 83 // Map the allocated memory, obtaining a virtual address. 84 85 *vaddr = 0; 86 87 if (ipc_new_dataspace(size, alloc_flags, align, mem)) 88 return -L4_ENOMEM; 89 90 if (ipc_attach_dataspace_align(*mem, size, attach_flags, align, (void **) vaddr)) 91 return -L4_ENOMEM; 92 93 // Obtain a physical address. 94 95 l4_size_t size_out = size; 96 *paddr = 0; 97 98 if (l4re_dma_space_map(dma, *mem | L4_CAP_FPAGE_RW, 0, &size_out, 0, 99 L4RE_DMA_SPACE_TO_DEVICE, paddr)) 100 return -L4_ENOMEM; 101 102 // Test the mapped region size. 103 104 if (size_out != size) 105 return -L4_ENOMEM; 106 107 return L4_EOK; 108 }