1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6#include <stdlib.h>
7#include <string.h>
8
9#include <sel4vm/guest_vm.h>
10#include <sel4vm/guest_vcpu_fault.h>
11
12#include <sel4vmmplatsupport/plat/vmct.h>
13#include <sel4vmmplatsupport/plat/devices.h>
14
15#define GWSTAT_TCON          (1U << 16)
16#define GWSTAT_COMP3_ADD_INC (1U << 14)
17#define GWSTAT_COMP3H        (1U << 13)
18#define GWSTAT_COMP3L        (1U << 12)
19#define GWSTAT_COMP2_ADD_INC (1U << 10)
20#define GWSTAT_COMP2H        (1U << 9)
21#define GWSTAT_COMP2L        (1U << 8)
22#define GWSTAT_COMP1_ADD_INC (1U << 6)
23#define GWSTAT_COMP1H        (1U << 5)
24#define GWSTAT_COMP1L        (1U << 4)
25#define GWSTAT_COMP0_ADD_INC (1U << 2)
26#define GWSTAT_COMP0H        (1U << 1)
27#define GWSTAT_COMP0L        (1U << 0)
28
29
30struct vmct_priv {
31    uint32_t wstat;
32    uint32_t cnt_wstat;
33    uint32_t lwstat[4];
34};
35
36static inline struct vmct_priv *vmct_get_priv(void *priv)
37{
38    assert(priv);
39    return (struct vmct_priv *)priv;
40}
41
42
43static memory_fault_result_t handle_vmct_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr, size_t fault_length,
44                                               void *cookie)
45{
46    struct vmct_priv *mct_priv;
47    int offset;
48    uint32_t mask;
49    struct device *dev = (struct device *)cookie;
50
51    mct_priv = vmct_get_priv(dev->priv);
52
53    /* Gather fault information */
54    offset = fault_addr - dev->pstart;
55    mask = get_vcpu_fault_data_mask(vcpu);
56    /* Handle the fault */
57    if (offset < 0x300) {
58        /*** Global ***/
59        uint32_t *wstat;
60        uint32_t *cnt_wstat;
61        wstat = &mct_priv->wstat;
62        cnt_wstat = &mct_priv->cnt_wstat;
63        if (!is_vcpu_read_fault(vcpu)) {
64            if (offset >= 0x100 && offset < 0x108) {
65                /* Count registers */
66                *cnt_wstat |= (1 << (offset - 0x100) / 4);
67            } else if (offset == 0x110) {
68                *cnt_wstat &= ~(get_vcpu_fault_data(vcpu) & mask);
69            } else if (offset >= 0x200 && offset < 0x244) {
70                /* compare registers */
71                *wstat |= (1 << (offset - 0x200) / 4);
72            } else if (offset == 0x24C) {
73                /* Write status */
74                *wstat &= ~(get_vcpu_fault_data(vcpu) & mask);
75            } else {
76                ZF_LOGD("global MCT fault on unknown offset 0x%x\n", offset);
77            }
78        } else {
79            /* read fault */
80            if (offset == 0x110) {
81                set_vcpu_fault_data(vcpu, *cnt_wstat);
82            } else if (offset == 0x24C) {
83                set_vcpu_fault_data(vcpu, *wstat);
84            } else {
85                set_vcpu_fault_data(vcpu, 0);
86            }
87        }
88    } else {
89        /*** Local ***/
90        int timer = (offset - 0x300) / 0x100;
91        int loffset = (offset - 0x300) - (timer * 0x100);
92        uint32_t *wstat = &mct_priv->lwstat[timer];
93        if (is_vcpu_read_fault(vcpu)) {
94            if (loffset == 0x0) {        /* tcompl */
95                *wstat |= (1 << 1);
96            } else if (loffset == 0x8) {  /* tcomph */
97                *wstat |= (1 << 1);
98            } else if (loffset == 0x20) { /* tcon */
99                *wstat |= (1 << 3);
100            } else if (loffset == 0x34) { /* int_en */
101                /* Do nothing */
102            } else if (loffset == 0x40) { /* wstat */
103                *wstat &= ~(get_vcpu_fault_data(vcpu) & mask);
104            } else {
105                ZF_LOGD("local MCT fault on unknown offset 0x%x\n", offset);
106            }
107        } else {
108            /* read fault */
109            if (loffset == 0x40) {
110                set_vcpu_fault_data(vcpu, *wstat);
111            } else {
112                set_vcpu_fault_data(vcpu, 0);
113            }
114        }
115    }
116    advance_vcpu_fault(vcpu);
117    return FAULT_HANDLED;
118}
119
120const struct device dev_vmct_timer = {
121    .name = "mct",
122
123    .pstart = MCT_ADDR,
124
125    .size = 0x1000,
126    .priv = NULL
127};
128
129
130int vm_install_vmct(vm_t *vm)
131{
132    struct vmct_priv *vmct_data;
133    struct device *d;
134    int err;
135
136    d = (struct device *)calloc(1, sizeof(struct device));
137    if (!d) {
138        return -1;
139    }
140    memcpy(d, &dev_vmct_timer, sizeof(struct device));
141    /* Initialise the virtual device */
142    vmct_data = calloc(1, sizeof(struct vmct_priv));
143    if (vmct_data == NULL) {
144        assert(vmct_data);
145        return -1;
146    }
147    d->priv = vmct_data;
148    vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d->pstart, d->size,
149                                                                handle_vmct_fault, (void *)d);
150    if (!reservation) {
151        free(d);
152        free(vmct_data);
153        return -1;
154    }
155    return 0;
156}
157