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/* IO port/device functionality. This is meant for interaction with
14 * libplatsupport infrastructure.
15 */
16
17#include <assert.h>
18#include <libfdt.h>
19#include <camkes/dataport.h>
20#include <camkes/dma.h>
21#include <camkes/io.h>
22#include <camkes/interface_registration.h>
23#include <camkes/irq.h>
24#include <camkes/arch/io.h>
25#include <platsupport/io.h>
26#include <platsupport/driver_module.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <utils/util.h>
30
31/* Force the _dataport_frames  section to be created even if no modules are defined. */
32static USED SECTION("_dataport_frames") struct {} dummy_dataport_frame;
33/* Definitions so that we can find the exposed dataport frames */
34extern dataport_frame_t __start__dataport_frames[];
35extern dataport_frame_t __stop__dataport_frames[];
36
37typedef struct camkes_defer_token {
38    char *device_path;
39    ps_driver_init_fn_t init_func;
40} camkes_defer_token_t;
41
42/* Basic linked-list implementation. */
43typedef struct ll_ {
44    void *data;
45    struct ll_ *next;
46} ll_t;
47
48#ifndef NDEBUG
49bool malloc_ops_initialised = false;
50static ps_malloc_ops_t io_mapper_malloc_ops = {0};
51#endif
52
53static UNUSED int ll_prepend(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data)
54{
55    ll_t *node = NULL;
56    int error = ps_calloc(malloc_ops, 1, sizeof * node, (void **) &node);
57    if (error) {
58        return -1;
59    }
60    node->data = (void *)data;
61    node->next = *list;
62    *list = node;
63    return 0;
64}
65
66static UNUSED int ll_append(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data)
67{
68    ll_t *node = NULL;
69    int error = ps_calloc(malloc_ops, 1, sizeof * node, (void **) &node);
70    if (error) {
71        return -1;
72    }
73    node->data = (void *)data;
74    node->next = NULL;
75    if (*list == NULL) {
76        *list = node;
77        return 0;
78    }
79    ll_t *curr = NULL;
80    for (curr = *list; curr->next != NULL; curr = curr->next);
81    curr->next = node;
82    return 0;
83}
84
85static UNUSED int ll_remove(ps_malloc_ops_t *malloc_ops, ll_t **list, const void *data)
86{
87    for (ll_t **l = list; *l != NULL; l = &(*l)->next) {
88        if ((*l)->data == data) {
89            /* found it */
90            ll_t *temp = *l;
91            *l = (*l)->next;
92            ps_free(malloc_ops, sizeof(*temp), temp);
93            return 0;
94        }
95    }
96    return -1;
97}
98
99typedef struct {
100    ps_malloc_ops_t *malloc_ops;
101    ps_io_map_fn_t map;
102    ll_t *mapped;
103} cookie_t;
104
105/* Debug wrapper for IO map. This function calls the underlying map function
106 * and tracks results for the purpose of catching illegal unmapping operations.
107 * Note that this function is unused when NDEBUG is defined.
108 */
109static UNUSED void *io_map(void *cookie, uintptr_t paddr, size_t size,
110                           int cached, ps_mem_flags_t flags)
111{
112
113    /* Call the real IO map function. */
114    cookie_t *c = cookie;
115    void *p = c->map(NULL, paddr, size, cached, flags);
116
117    if (p != NULL) {
118        /* The IO map function gave us a successful result; track this pointer
119         * to lookup during unmapping.
120         */
121        if (ll_prepend(c->malloc_ops, &c->mapped, p) != 0) {
122            LOG_ERROR("failed to track mapped IO pointer %p\n", p);
123        }
124    }
125
126    return p;
127}
128
129static int UNUSED pointer_compare(void *a, void *b)
130{
131    uintptr_t p = (uintptr_t)a;
132    uintptr_t q = (uintptr_t)b;
133    if (p > q) {
134        return 1;
135    } else if (p < q) {
136        return -1;
137    } else {
138        return 0;
139    }
140}
141
142static void *camkes_io_map(void *cookie UNUSED, uintptr_t paddr,
143                           size_t size, int cached UNUSED, ps_mem_flags_t flags UNUSED)
144{
145    if (paddr % PAGE_SIZE_4K != 0 && size % PAGE_SIZE_4K != 0) {
146        ZF_LOGE("paddr or size has incorrect alignment: (%p, 0x%zx)", (void *) paddr, size);
147        return NULL;
148    }
149
150    /* Given a base paddr and size, we try to find a region of mapped memory that
151     * is a superset of the given parameters. */
152    size_t size_counter = 0;
153    bool counting_frames = false;
154    uintptr_t base_vaddr = 0;
155    for (dataport_frame_t *frame = __start__dataport_frames;
156         frame < __stop__dataport_frames; frame++) {
157        if (counting_frames) {
158            if (paddr == (frame->paddr - size_counter)) {
159                size_counter += frame->size;
160            } else {
161                /* We've encountered a different region of physical memory that does
162                   not match what we want, reset the counters */
163                counting_frames = false;
164                base_vaddr = 0;
165                size_counter = 0;
166            }
167        } else {
168            if (paddr >= frame->paddr && (frame->paddr + frame->size) > paddr) {
169                /* We've found the first frame of the mapped region,
170                   start counting from here */
171                counting_frames = true;
172                base_vaddr = frame->vaddr + (paddr - frame->paddr);
173                size_counter += (frame->vaddr + frame->size) - base_vaddr;
174            }
175        }
176
177        if (size_counter >= size) {
178            /* We've found all the frames that cover the desired region */
179            return (void *)base_vaddr;
180        }
181    }
182
183    /* Not found. */
184    return NULL;
185}
186
187/* We never unmap anything. */
188static void io_unmap(void *cookie UNUSED, void *vaddr UNUSED, size_t size UNUSED)
189{
190#ifndef NDEBUG
191    cookie_t *c = cookie;
192    /* Make sure we previously mapped the pointer the caller gave us. */
193    if (ll_remove(c->malloc_ops, &c->mapped, vaddr) != 0) {
194        LOG_ERROR("unmapping an IO pointer that was not previously mapped: %p\n",
195                  vaddr);
196    }
197#endif
198}
199
200int camkes_io_mapper(ps_io_mapper_t *mapper)
201{
202    if (mapper == NULL) {
203        ZF_LOGE("mapper is NULL");
204        return -1;
205    }
206#ifdef NDEBUG
207    mapper->cookie = NULL;
208    mapper->io_map_fn = camkes_io_map;
209#else
210    if (!malloc_ops_initialised) {
211        ZF_LOGF_IF(camkes_ps_malloc_ops(&io_mapper_malloc_ops),
212                   "Failed to get malloc_ops for DEBUG mode io mapper");
213        malloc_ops_initialised = true;
214    }
215    cookie_t *c = NULL;
216    int error = ps_calloc(&io_mapper_malloc_ops, 1, sizeof(*c), (void **) &c);
217    if (error) {
218        return -1;
219    }
220    c->malloc_ops = &io_mapper_malloc_ops;
221    c->map = camkes_io_map;
222    c->mapped = NULL;
223    mapper->cookie = c;
224    mapper->io_map_fn = io_map;
225#endif
226    mapper->io_unmap_fn = io_unmap;
227    return 0;
228}
229
230static int camkes_io_port_in(void *cookie UNUSED, uint32_t port, int io_size, uint32_t *result)
231{
232    return camkes_arch_io_port_in(port, io_size, result);
233}
234
235static int camkes_io_port_out(void *cookie UNUSED, uint32_t port, int io_size, uint32_t val)
236{
237    return camkes_arch_io_port_out(port, io_size, val);
238}
239
240int camkes_io_port_ops(ps_io_port_ops_t *ops)
241{
242    if (ops == NULL) {
243        ZF_LOGE("ops is NULL");
244        return -1;
245    }
246    ops->io_port_in_fn = camkes_io_port_in;
247    ops->io_port_out_fn = camkes_io_port_out;
248    return 0;
249}
250
251int camkes_ps_malloc_ops(ps_malloc_ops_t *ops)
252{
253    if (ops == NULL) {
254        ZF_LOGE("ops is NULL");
255        return -1;
256    }
257
258    int ret = ps_new_stdlib_malloc_ops(ops);
259    if (ret) {
260        return ret;
261    }
262
263#ifndef NDEBUG
264    /* This works as malloc_ops contains pointers */
265    malloc_ops_initialised = true;
266    io_mapper_malloc_ops = (ps_malloc_ops_t) * ops;
267#endif
268
269    return 0;
270}
271
272static char *camkes_io_fdt_get(void *cookie)
273{
274    return (char *)(cookie ? cookie : NULL);
275}
276
277int camkes_io_fdt(ps_io_fdt_t *io_fdt)
278{
279    if (io_fdt == NULL) {
280        ZF_LOGE("io_fdt is NULL");
281        return -1;
282    }
283
284    extern char *dtb_symbol WEAK;
285
286    if (!&dtb_symbol) {
287        io_fdt->cookie = NULL;
288    } else {
289        /* the buffer contains the bootinfo header, so we skip it */
290        io_fdt->cookie = (void *) &dtb_symbol + sizeof(seL4_BootInfoHeader);
291    }
292
293    io_fdt->get_fn = camkes_io_fdt_get;
294
295    return 0;
296}
297
298/* Force the _driver_modules section to be created even if no modules are defined. */
299static USED SECTION("_driver_modules") struct {} dummy_driver_module;
300/* Definitions so that we can find the list of driver modules that can be initialised */
301extern ps_driver_module_t *__start__driver_modules[];
302extern ps_driver_module_t *__stop__driver_modules[];
303
304static ll_t *driver_defer_list = NULL;
305
306static int defer_driver_init(ps_io_ops_t *ops, char *device_path, ps_driver_init_fn_t init_func)
307{
308    if (ops == NULL) {
309        ZF_LOGE("ops is NULL");
310        return -1;
311    }
312
313    if (device_path == NULL) {
314        ZF_LOGE("device path is NULL");
315        return -1;
316    }
317
318    if (init_func == NULL) {
319        ZF_LOGE("init_func is NULL");
320        return -1;
321    }
322
323    camkes_defer_token_t *defer_token = NULL;
324    int error = ps_calloc(&ops->malloc_ops, 1, sizeof(*defer_token), (void **) &defer_token);
325    if (error) {
326        ZF_LOGE("Failed to allocate memory for a bookkeeping structure for the driver defer list");
327        return -1;
328    }
329
330    defer_token->device_path = device_path;
331    defer_token->init_func = init_func;
332
333    error = ll_append(&ops->malloc_ops, &driver_defer_list, defer_token);
334    if (error) {
335        ZF_LOGE("Failed to add the driver init function to the defer list");
336    }
337
338    return 0;
339}
340
341static int find_compatible_driver_module(ps_io_ops_t *ops, int node_offset, char *device_path)
342{
343    if (ops == NULL) {
344        ZF_LOGF("ops is NULL");
345    }
346
347    char *dtb_blob = ps_io_fdt_get(&ops->io_fdt);
348    if (!dtb_blob) {
349        ZF_LOGF("No DTB supplied!");
350    }
351
352    /* Look through the list of hardware modules that are registered and
353     * pick the most suitable one by comparing the module's compatible
354     * strings against the device node's */
355    for (ps_driver_module_t **module = __start__driver_modules; module < __stop__driver_modules; module++) {
356        for (const char **curr_str = (*module)->compatible_list; *curr_str != NULL; curr_str++) {
357            int match_ret = fdt_node_check_compatible(dtb_blob, node_offset, *curr_str);
358            if (match_ret == 0) {
359                /* Found a match! */
360                int ret = (*module)->init(ops, device_path);
361                if (ret == PS_DRIVER_INIT_DEFER) {
362                    ZF_LOGF_IF(defer_driver_init(ops, device_path, (*module)->init),
363                               "Failed to defer the initialisation of node %s", device_path);
364                    return 0;
365                } else if (ret < 0) {
366                    ZF_LOGE("Node pointed to by path %s failed to have a driver initialise properly, ignoring", device_path);
367                    return -1;
368                } else if (ret == PS_DRIVER_INIT_SUCCESS) {
369                    return 0;
370                } else {
371                    ZF_LOGE("Initialisation function for node pointed to by path %s returned an unexpected code", device_path);
372                    return -1;
373                }
374            } else if (match_ret < 0) {
375                ZF_LOGE("Node pointed to by path %s is malformed, ignoring", device_path);
376                return -1;
377            }
378            /* Ignore the no match case */
379        }
380    }
381
382    /* No suitable module was found for this device node */
383    ZF_LOGE("No suitable driver was found for path %s, ignoring", device_path);
384    return 0;
385}
386
387static int handle_defered_modules(ps_io_ops_t *ops)
388{
389    if (ops == NULL) {
390        ZF_LOGF("ops is NULL");
391    }
392
393    /* The set of modules that have deferred and that we need to work through */
394    ll_t *working_set = driver_defer_list;
395    /* The set of modules that have deferred again */
396    ll_t *deferred_set = NULL;
397    ll_t *deferred_tail = NULL;
398
399    while (working_set != NULL) {
400        ll_t *curr = working_set;
401        while (curr != NULL) {
402            camkes_defer_token_t *defer_token = curr->data;
403            int ret = defer_token->init_func(ops, defer_token->device_path);
404            if (ret == PS_DRIVER_INIT_DEFER) {
405                /* Throw this into deferred set and also remove it from the
406                 * working set */
407                if (deferred_tail != NULL) {
408                    deferred_tail->next = curr;
409                }
410                if (deferred_set == NULL) {
411                    deferred_set = curr;
412                }
413                deferred_tail = curr;
414                curr = curr->next;
415                deferred_tail->next = NULL;
416            } else {
417                /* Diagnostics */
418                if (ret < 0) {
419                    ZF_LOGE("Node pointed to by path %s failed to have a driver initialise properly, ignoring",
420                            defer_token->device_path);
421                } else if (ret != PS_DRIVER_INIT_SUCCESS) {
422                    ZF_LOGE("Initialisation function for node pointed to by path %s returned an unexpected code",
423                            defer_token->device_path);
424                }
425                /* Remove this from the working set and deallocate memory */
426                ll_t *temp = curr;
427                curr = curr->next;
428                ZF_LOGF_IF(ps_free(&ops->malloc_ops, sizeof(*defer_token), defer_token),
429                           "Failed to deallocate a camkes_defer_token_t instance");
430                ZF_LOGF_IF(ps_free(&ops->malloc_ops, sizeof(*temp), temp), "Failed to deallocate a ll_t node");
431            }
432        }
433        /* Swap the two sets and continue on */
434        working_set = deferred_set;
435        deferred_set = NULL;
436        deferred_tail = NULL;
437    }
438
439    return 0;
440}
441
442/* Force the _hardware_init section to be created even if no modules are defined. */
443static USED SECTION("_hardware_init") struct {} dummy_init_module;
444/* Definitions so that we can find the list of hardware modules that we need to initialise */
445extern char **__start__hardware_init[];
446extern char **__stop__hardware_init[];
447
448int camkes_call_hardware_init_modules(ps_io_ops_t *ops)
449{
450    if (__start__hardware_init == __stop__hardware_init) {
451        /* Exit early if there are no modules to initialise */
452        return 0;
453    }
454
455    if (ops == NULL) {
456        ZF_LOGE("ops is NULL");
457        return -1;
458    }
459
460    char *dtb_blob = ps_io_fdt_get(&ops->io_fdt);
461    if (!dtb_blob) {
462        ZF_LOGE("No DTB supplied!");
463        return -1;
464    }
465
466    for (char ***curr_path = __start__hardware_init; curr_path < __stop__hardware_init; curr_path++) {
467        int node_offset = fdt_path_offset(dtb_blob, **curr_path);
468        if (node_offset < 0) {
469            ZF_LOGE("Path %s doesn't seem to be in the DTB, ignoring", **curr_path);
470            continue;
471        }
472
473        /* Quick sanity check to see if the node has a compatible property */
474        const void *dummy = fdt_getprop(dtb_blob, node_offset, "compatible", NULL);
475        if (dummy == NULL) {
476            ZF_LOGE("Node pointed to by path %s doesn't have a compatible string or is malformed, ignoring", **curr_path);
477            continue;
478        }
479
480        /* Currently most of the errors in this function as they aren't fatal */
481        find_compatible_driver_module(ops, node_offset, **curr_path);
482    }
483
484    /* Now take care of the deferred modules, again ignore most of the errors
485     * in this function as they aren't fatal */
486    handle_defered_modules(ops);
487
488    return 0;
489}
490
491int camkes_io_ops(ps_io_ops_t *ops)
492{
493    if (ops == NULL) {
494        ZF_LOGE("ops is NULL");
495        return -1;
496    }
497
498    int ret = camkes_ps_malloc_ops(&ops->malloc_ops);
499    if (ret) {
500        return ret;
501    }
502
503    ret = camkes_io_mapper(&ops->io_mapper) ||
504          camkes_io_port_ops(&ops->io_port_ops) ||
505          camkes_dma_manager(&ops->dma_manager) ||
506          camkes_io_fdt(&ops->io_fdt) ||
507          camkes_irq_ops(&ops->irq_ops) ||
508          camkes_interface_registration_ops(&ops->interface_registration_ops, &ops->malloc_ops);
509
510    return ret;
511}
512