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