1/*
2 * Copyright 2019, 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 <camkes/dataport.h>
14#include <camkes/arch/dataport.h>
15#include <platsupport/io.h>
16#include <sel4/sel4.h>
17#include <utils/util.h>
18
19static int sel4_cache_op(seL4_CPtr frame_cap, seL4_Word start, seL4_Word end, dma_cache_op_t cache_op)
20{
21    switch (cache_op) {
22    case DMA_CACHE_OP_CLEAN:
23        return seL4_ARM_Page_Clean_Data(frame_cap, start, end);
24    case DMA_CACHE_OP_INVALIDATE:
25        return seL4_ARM_Page_Invalidate_Data(frame_cap, start, end);
26    case DMA_CACHE_OP_CLEAN_INVALIDATE:
27        return seL4_ARM_Page_CleanInvalidate_Data(frame_cap, start, end);
28    default:
29        ZF_LOGF("Invalid cache_op %d", cache_op);
30        return -1;
31    }
32}
33
34int camkes_dataport_arch_flush_cache(size_t start_offset, size_t size,
35                                     uintptr_t dataport_start, size_t dataport_size,
36                                     dma_cache_op_t cache_op)
37{
38    if (start_offset >= dataport_size || size > dataport_size || dataport_size - size < start_offset) {
39        ZF_LOGE("Specified range is outside the bounds of the dataport");
40        return -1;
41    }
42
43    size_t current_offset = start_offset;
44    size_t end_offset = start_offset + size;
45
46    /* Find the region that we want to flush */
47    for (dataport_frame_t *frame = __start__dataport_frames;
48         frame < __stop__dataport_frames; frame++) {
49        if (frame->vaddr == dataport_start) {
50            /* Find the frame that we want to start flushing from */
51            size_t page_size_of_region = frame->size;
52            dataport_frame_t *curr_frame = frame + (current_offset / page_size_of_region);
53
54            while (current_offset < end_offset) {
55                size_t frame_top = MIN(ROUND_UP(current_offset + 1, page_size_of_region), end_offset);
56                size_t frame_start_offset = current_offset % page_size_of_region;
57                size_t frame_end_offset = ((frame_top - 1) % page_size_of_region);
58                int error = sel4_cache_op(curr_frame->cap, frame_start_offset, frame_end_offset, cache_op);
59                if (error) {
60                    ZF_LOGE("Cache flush syscall returned with error: %d", error);
61                    return error;
62                }
63                current_offset = frame_top;
64                curr_frame++;
65            }
66
67            /* We've done what we needed to do, no need to keep looping over dataport frames */
68            break;
69        }
70    }
71
72    return 0;
73}
74