1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/util/src/dma.cc Tue Oct 24 17:20:58 2023 +0200
1.3 @@ -0,0 +1,108 @@
1.4 +/*
1.5 + * DMA-related memory allocation utility functions.
1.6 + *
1.7 + * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/re/c/dataspace.h>
1.26 +#include <l4/re/c/dma_space.h>
1.27 +#include <l4/re/c/mem_alloc.h>
1.28 +#include <l4/re/env.h>
1.29 +#include <l4/re/protocols.h>
1.30 +#include <l4/sys/factory.h>
1.31 +#include <l4/sys/types.h>
1.32 +#include <l4/vbus/vbus.h>
1.33 +
1.34 +#include <ipc/cap_alloc.h>
1.35 +#include <ipc/mem_ipc.h>
1.36 +
1.37 +#include "dma.h"
1.38 +#include "memory.h"
1.39 +
1.40 +
1.41 +
1.42 +// Allocate a memory region of the given size for DMA.
1.43 +
1.44 +long get_dma_region(unsigned long size, int align, l4_addr_t *vaddr,
1.45 + l4re_dma_space_dma_addr_t *paddr, l4_cap_idx_t *mem)
1.46 +{
1.47 + // Memory allocation capabilities.
1.48 +
1.49 + l4_cap_idx_t dma, vbus;
1.50 +
1.51 + // Obtain capabilities for the DMA region and the vbus.
1.52 +
1.53 + dma = ipc_cap_alloc();
1.54 +
1.55 + if (l4_is_invalid_cap(dma))
1.56 + return -L4_ENOENT;
1.57 +
1.58 + vbus = l4re_env_get_cap("vbus");
1.59 +
1.60 + if (l4_is_invalid_cap(vbus))
1.61 + return -L4_ENOENT;
1.62 +
1.63 + // Create the DMA space.
1.64 +
1.65 + if (l4_error(l4_factory_create(l4re_env()->mem_alloc, L4RE_PROTO_DMA_SPACE, dma)))
1.66 + return -L4_ENOMEM;
1.67 +
1.68 + // Find the DMA domain and assign the DMA space.
1.69 +
1.70 + l4vbus_device_handle_t device = L4VBUS_NULL;
1.71 + l4vbus_resource_t dma_resource;
1.72 +
1.73 + if (!find_resource(&device, &dma_resource, L4VBUS_RESOURCE_DMA_DOMAIN))
1.74 + return -L4_ENOENT;
1.75 +
1.76 + if (l4vbus_assign_dma_domain(vbus, dma_resource.start,
1.77 + L4VBUS_DMAD_BIND | L4VBUS_DMAD_L4RE_DMA_SPACE,
1.78 + dma))
1.79 + return -L4_ENOMEM;
1.80 +
1.81 + // Allocate memory at the given alignment.
1.82 +
1.83 + const l4_size_t alloc_flags = L4RE_MA_CONTINUOUS | L4RE_MA_PINNED;
1.84 + const l4re_rm_flags_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RW;
1.85 +
1.86 + // Map the allocated memory, obtaining a virtual address.
1.87 +
1.88 + *vaddr = 0;
1.89 +
1.90 + if (ipc_new_dataspace(size, alloc_flags, align, mem))
1.91 + return -L4_ENOMEM;
1.92 +
1.93 + if (ipc_attach_dataspace_align(*mem, size, attach_flags, align, (void **) vaddr))
1.94 + return -L4_ENOMEM;
1.95 +
1.96 + // Obtain a physical address.
1.97 +
1.98 + l4_size_t size_out = size;
1.99 + *paddr = 0;
1.100 +
1.101 + if (l4re_dma_space_map(dma, *mem | L4_CAP_FPAGE_RW, 0, &size_out, 0,
1.102 + L4RE_DMA_SPACE_TO_DEVICE, paddr))
1.103 + return -L4_ENOMEM;
1.104 +
1.105 + // Test the mapped region size.
1.106 +
1.107 + if (size_out != size)
1.108 + return -L4_ENOMEM;
1.109 +
1.110 + return L4_EOK;
1.111 +}