1/*
2 * Copyright 2017, 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 <sel4platsupport/gen_config.h>
15#include <vka/object.h>
16#include <vka/vka.h>
17#include <vka/capops.h>
18#include <sel4platsupport/timer.h>
19#include <platsupport/ltimer.h>
20#include <sel4platsupport/device.h>
21#include <sel4platsupport/io.h>
22#include <utils/util.h>
23
24static void cleanup_timer_irq(vka_t *vka, sel4ps_irq_t *irq)
25{
26    seL4_IRQHandler_Clear(irq->handler_path.capPtr);
27    /* clear the cslots */
28    vka_cnode_delete(&irq->badged_ntfn_path);
29    vka_cnode_delete(&irq->handler_path);
30    /* free the cslots */
31    vka_cspace_free(vka, irq->badged_ntfn_path.capPtr);
32    vka_cspace_free(vka, irq->handler_path.capPtr);
33}
34
35
36void sel4platsupport_destroy_timer(seL4_timer_t *timer, vka_t *vka)
37{
38    ltimer_destroy(&timer->ltimer);
39    assert(timer->to.nirqs < MAX_IRQS);
40    for (size_t i = 0; i < timer->to.nirqs; i++) {
41        cleanup_timer_irq(vka, &timer->to.irqs[i]);
42    }
43
44    assert(timer->to.nobjs < MAX_OBJS);
45    for (size_t i = 0; i < timer->to.nobjs; i++) {
46        vka_free_object(vka, &timer->to.objs[i].obj);
47    }
48}
49
50void sel4platsupport_handle_timer_irq(seL4_timer_t *timer, seL4_Word badge)
51{
52    assert(timer->to.nirqs < MAX_IRQS);
53    /* check which bits are set to find which irq to handle */
54    for (unsigned long i = 0; badge && i < timer->to.nirqs; i++) {
55        /* invert the bit: we take the top badge bits to identify timers */
56        long irq = seL4_BadgeBits - i - 1;
57        if (badge & BIT(irq)) {
58            /* mask the bit out of the badge */
59            badge &= ~BIT(irq);
60            if (timer->to.irqs[i].irq.type != PS_NONE) {
61                int error = seL4_IRQHandler_Ack(timer->to.irqs[i].handler_path.capPtr);
62                if (error) {
63                    ZF_LOGE("Failed to ack irq %lu, error %d", irq, error);
64                }
65            }
66        }
67    }
68}
69
70static int setup_irq(vka_t *vka, sel4ps_irq_t *irq, seL4_Word badge, seL4_CPtr ntfn)
71{
72    int error = vka_cspace_alloc_path(vka, &irq->badged_ntfn_path);
73    cspacepath_t path;
74    vka_cspace_make_path(vka, ntfn, &path);
75    if (!error) {
76        /* badge it */
77        error = vka_cnode_mint(&irq->badged_ntfn_path, &path, seL4_AllRights, badge);
78    }
79    if (!error) {
80        /* set notification *before* acking any pending IRQ to ensure there is no race where
81         * we lose an IRQ */
82        error =  seL4_IRQHandler_SetNotification(irq->handler_path.capPtr, irq->badged_ntfn_path.capPtr);
83    }
84    if (!error) {
85        error = seL4_IRQHandler_Ack(irq->handler_path.capPtr);
86    }
87    return error;
88}
89
90static int get_nth_pmem(vka_t *vka, ltimer_t *ltimer, sel4ps_pmem_t *obj, int n)
91{
92    int error = ltimer_get_nth_pmem(ltimer, n, &obj->region);
93    if (!error && obj->region.length > PAGE_SIZE_4K) {
94        ZF_LOGE("Support for timers with anything but 4K pages unimplemented! length %zu",
95                (size_t) obj->region.length);
96        return ENOSYS;
97    }
98
99    obj->region.length = PAGE_SIZE_4K;
100    if (!error) {
101        error = vka_alloc_untyped_at(vka, seL4_PageBits, obj->region.base_addr,
102                                     &obj->obj);
103    }
104    obj->obj.size_bits = seL4_PageBits;
105    if (error) {
106        ZF_LOGF("Failed to obtain device-ut cap for default timer.");
107    }
108    return error;
109}
110
111static inline size_t get_nirqs(ltimer_t *ltimer)
112{
113    size_t nirqs = ltimer_get_num_irqs(ltimer);
114    if (nirqs > MAX_IRQS) {
115        ZF_LOGE("MAX_IRQS insufficient for timer");
116        return -1;
117    }
118
119    return nirqs;
120}
121
122static int init_timer_internal(vka_t *vka, simple_t *simple, seL4_CPtr ntfn,
123                               seL4_timer_t *timer, size_t nirqs)
124{
125
126    /* set up the irq caps the timer needs */
127    timer->to.nirqs = 0;
128    for (size_t i = 0; i < nirqs; i++) {
129        assert(timer->to.irqs[i].irq.type != PS_NONE);
130        int error = sel4platsupport_copy_irq_cap(vka, simple, &timer->to.irqs[i].irq,
131                                                 &timer->to.irqs[i].handler_path);
132        if (!error) {
133            error = setup_irq(vka, &timer->to.irqs[i], BIT(seL4_BadgeBits - i - 1), ntfn);
134        }
135        if (error) {
136            sel4platsupport_destroy_timer(timer, vka);
137            return error;
138        }
139        /* increment as we go to aid destruction */
140        timer->to.nirqs++;
141    }
142
143    timer->to.nobjs = 0;
144    return 0;
145}
146
147int sel4platsupport_init_default_timer_ops(vka_t *vka, UNUSED vspace_t *vspace, simple_t *simple,
148                                           ps_io_ops_t ops, seL4_CPtr ntfn, seL4_timer_t *timer)
149{
150    int error;
151    if (timer == NULL) {
152        return EINVAL;
153    }
154
155    error = ltimer_default_describe(&timer->ltimer, ops);
156
157    if (!error) {
158        size_t nirqs = get_nirqs(&timer->ltimer);
159        for (size_t i = 0; i < nirqs; i++) {
160            error = ltimer_get_nth_irq(&timer->ltimer, i, &timer->to.irqs[i].irq);
161            if (error) {
162                return error;
163            }
164        }
165        error = init_timer_internal(vka, simple, ntfn, timer, nirqs);
166    }
167
168    if (!error)  {
169        error = ltimer_default_init(&timer->ltimer, ops, NULL, NULL);
170    }
171    return error;
172}
173
174int sel4platsupport_init_default_timer(vka_t *vka, vspace_t *vspace, simple_t *simple,
175                                       seL4_CPtr ntfn, seL4_timer_t *timer)
176{
177    int error;
178
179    /* initialise io ops */
180    ps_io_ops_t ops;
181    memset(&ops, 0, sizeof(ops));
182    error = sel4platsupport_new_io_ops(vspace, vka, simple, &ops);
183    if (!error) {
184        /* we have no way of storing the fact that we allocated these io ops so we'll just leak
185         * them forever */
186        return sel4platsupport_init_default_timer_ops(vka, vspace, simple, ops, ntfn, timer);
187    }
188    return error;
189}
190
191int sel4platsupport_init_timer_irqs(vka_t *vka, simple_t *simple,
192                                    seL4_CPtr ntfn, seL4_timer_t *timer, timer_objects_t *to)
193{
194    if (timer == NULL) {
195        return EINVAL;
196    }
197
198    /* copy the timer objects */
199    timer->to = *to;
200    return init_timer_internal(vka, simple, ntfn, timer, to->nirqs);
201}
202
203
204int sel4platsupport_init_default_timer_caps(vka_t *vka, vspace_t *vspace, simple_t *simple,
205                                            timer_objects_t *timer_objects)
206{
207    /* initialise io ops */
208    ps_io_ops_t ops;
209    memset(&ops, 0, sizeof(ops));
210    int error = sel4platsupport_new_io_ops(vspace, vka, simple, &ops);
211    if (error) {
212        ZF_LOGE("Failed to get io ops");
213        return error;;
214    }
215
216    /* Allocate timer irqs. */
217    ltimer_t ltimer;
218    error = ltimer_default_describe(&ltimer, ops);
219    if (error) {
220        ZF_LOGE("Failed to describe default timer");
221        return error;
222    }
223
224    /* set up the irq caps the timer needs */
225    size_t nirqs = get_nirqs(&ltimer);
226    for (size_t i = 0; i < nirqs; i++) {
227        error = ltimer_get_nth_irq(&ltimer, i, &timer_objects->irqs[i].irq);
228        assert(timer_objects->irqs[i].irq.type != PS_NONE);
229        if (!error) {
230            error =  sel4platsupport_copy_irq_cap(vka, simple, &timer_objects->irqs[i].irq,
231                                                  &timer_objects->irqs[i].handler_path);
232        }
233        if (error) {
234            ZF_LOGE("Failed to get irq cap %zu", i);
235            return error;
236        }
237        timer_objects->nirqs++;
238    }
239
240    /* Obtain untyped frame caps for PS default ltimer.
241     * currently this code assumes all timers need a 4k frame.
242     */
243    size_t nobjs = ltimer_get_num_pmems(&ltimer);
244    if (nobjs > MAX_OBJS) {
245        ZF_LOGE("MAX_OBJS insufficient for timer");
246        return -1;
247    }
248
249    for (size_t i = 0; i < nobjs; i++) {
250        error = get_nth_pmem(vka, &ltimer, &timer_objects->objs[i], i);
251        timer_objects->objs[i].obj.size_bits = seL4_PageBits;
252        if (error) {
253            ZF_LOGE("Failed to get nth pmem");
254            return error;
255        }
256        /* increment as we go to aid destruction */
257        timer_objects->nobjs++;
258    }
259
260    return error;
261}
262
263seL4_CPtr sel4platsupport_timer_objs_get_irq_cap(timer_objects_t *to, int id, irq_type_t type)
264{
265    for (size_t i = 0; i < to->nirqs; i++) {
266        if (to->irqs[i].irq.type == type) {
267            switch (type) {
268            case PS_MSI:
269                if (to->irqs[i].irq.msi.vector == id) {
270                    return to->irqs[i].handler_path.capPtr;
271                }
272                break;
273            case PS_IOAPIC:
274                if (to->irqs[i].irq.ioapic.pin == id) {
275                    return to->irqs[i].handler_path.capPtr;
276                }
277                break;
278            case PS_INTERRUPT:
279                if (to->irqs[i].irq.irq.number == id) {
280                    return to->irqs[i].handler_path.capPtr;
281                }
282                break;
283            case PS_TRIGGER:
284                if (to->irqs[i].irq.trigger.number == id) {
285                    return to->irqs[i].handler_path.capPtr;
286                }
287                break;
288            case PS_NONE:
289                ZF_LOGE("Invalid irq type");
290                break;
291            default:
292                ZF_LOGE("Unsupported irq type");
293            }
294        }
295    }
296
297    ZF_LOGE("Could not find irq");
298    return seL4_CapNull;
299}
300