1/* 2 * Copyright 2020, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <autoconf.h> 14#include <camkes.h> 15#include <camkes/dma.h> 16#include <platsupport/io.h> 17#include <platsupport/irq.h> 18#include <vka/vka.h> 19#include <simple/simple.h> 20#include <simple/simple_helpers.h> 21#include <allocman/vka.h> 22#include <sel4utils/vspace.h> 23#include <sel4utils/iommu_dma.h> 24#include <sel4platsupport/arch/io.h> 25 26seL4_CPtr(*original_vspace_get_cap)(vspace_t *, void *); 27 28/* Returns the cap to the frame mapped to vaddr, assuming 29 * vaddr points inside our dma pool. */ 30seL4_CPtr get_dma_frame_cap(vspace_t *vspace, void *vaddr) 31{ 32 seL4_CPtr cap = camkes_dma_get_cptr(vaddr); 33 if (cap == seL4_CapNull) { 34 return original_vspace_get_cap(vspace, vaddr); 35 } 36 return cap; 37} 38 39static USED SECTION("_dma_frames") struct {} dummy_dma_frame; 40extern dma_frame_t *__start__dma_frames[]; 41extern dma_frame_t *__stop__dma_frames[]; 42 43 44static uintptr_t dma_pin(void *cookie, void *addr, size_t size) 45{ 46 return (uintptr_t)addr; 47} 48 49 50int pc99_iospace_setup(ps_io_ops_t *io_ops, vka_t *vka, simple_t *camkes_simple, vspace_t *vspace) 51{ 52 int error = 0; 53 int pci_bdf_int = 0; 54 int bus, dev, fun = 0; 55 uint16_t iospace_id = 0; 56 /* component attribute describing iospace */ 57 const char *iospace_config = "/*? configuration[me.instance.name].get('iospaces', '')?*/"; 58 if (strlen(iospace_config) == 0) { 59 return 0; 60 } 61 ps_dma_man_t iospace_dma = {0}; 62 cspacepath_t iospace = {0}; 63 error = vka_cspace_alloc_path(vka, &iospace); 64 if (error) { 65 return error; 66 } 67 sscanf(iospace_config, "%hx:%x:%x.%d", &iospace_id, &bus, &dev, &fun); 68 pci_bdf_int = bus * 256 + dev * 8 + fun; 69 /* get this from the configuration */ 70 error = simple_get_iospace(camkes_simple, iospace_id, pci_bdf_int, &iospace); 71 if (error) { 72 ZF_LOGE("Could not locate IOSpace: %s\n", iospace_config); 73 return error; 74 } 75 76 /* Save a pointer to the original get_cap function for our vspace */ 77 original_vspace_get_cap = vspace->get_cap; 78 79 /* The iommu driver needs the caps to frames backing the dma buffer. 80 * It will invoke the get_cap method of its vspace to get these caps. 81 * Since the vspace we give to the iommu driver wasn't used to allocate 82 * the dma buffer, it doesn't know the caps to the frames backing the 83 * buffer. CAmkES allocated the buffer statically, and so the caps are 84 * known to it. Here, we override the get_cap method of our vspace to 85 * return dma buffer frame caps provided by CAmkES. */ 86 vspace->get_cap = get_dma_frame_cap; 87 88 error = sel4utils_make_iommu_dma_alloc(vka, vspace, &iospace_dma, 1, &iospace.capPtr); 89 if (error) { 90 return error; 91 } 92 93 for (dma_frame_t **frame = __start__dma_frames; 94 frame < __stop__dma_frames; frame++) { 95 int error = sel4utils_iommu_dma_alloc_iospace(iospace_dma.cookie, (void *)(*frame)->vaddr, (*frame)->size); 96 if (error) { 97 ZF_LOGE("Failed to setup IOMMU for DMA region: (%p, 0x%lx bytes)", (void *)(*frame)->vaddr, (*frame)->size); 98 return error; 99 } 100 } 101 102 io_ops->dma_manager.dma_pin_fn = dma_pin; 103 104 105 return 0; 106} 107 108CAMKES_DYNAMIC_INIT_MODULE_DEFINE(pc99_iospace_setup) 109