1/**
2 * \brief Memory management helper functions for device drivers.
3 */
4#include <stdio.h>
5#include <stdlib.h>
6#include <assert.h>
7
8#include <barrelfish/barrelfish.h>
9#include <barrelfish/capabilities.h>
10
11#include <driverkit/driverkit.h>
12
13#define UNBITS_GENPA(bits) (((genpaddr_t)1) << (bits))
14
15/**
16 * \brief Maps device register with the capabilities provided by the
17 * argcn slot.
18 *
19 * The function is used mostly as a helper to map registers by programs
20 * that were spawned by Kaluga.
21 *
22 * \param[in] address The address of the device region you want to map.
23 * \param[in] size The size of the region.
24 * \param[out] return_address The virtual memory address where the region
25 * was mapped at.
26 *
27 * \retval SYS_ERR_OK Mapping was succesful.
28 */
29errval_t map_device_register(lpaddr_t address, size_t size, lvaddr_t *return_address)
30{
31    errval_t err;
32    struct cnoderef argcn_cnref = {
33        .croot = CPTR_ROOTCN,
34        .cnode = ROOTCN_SLOT_ADDR(ROOTCN_SLOT_ARGCN),
35        .level = CNODE_TYPE_OTHER,
36    };
37
38    struct capref device_cap_iter = {
39        .cnode = argcn_cnref,
40        .slot = 0
41    };
42
43    for (; device_cap_iter.slot < L2_CNODE_SLOTS; device_cap_iter.slot++) {
44        // Get cap data
45        struct capability cap;
46        err = debug_cap_identify(device_cap_iter, &cap);
47        // If cap type was Null, kernel returns error
48        if (err_no(err) == SYS_ERR_IDENTIFY_LOOKUP ||
49            err_no(err) == SYS_ERR_CAP_NOT_FOUND ||
50            err_no(err) == SYS_ERR_LMP_CAPTRANSFER_SRC_LOOKUP) {
51            continue;
52        }
53
54        struct frame_identity fid;
55        err = frame_identify(device_cap_iter, &fid);
56        if (err_is_fail(err)) {
57            DEBUG_ERR(err, "Failure in frame_identify");
58            return err;
59        }
60        assert(err_is_ok(err));
61
62        lpaddr_t address_base = address & ~(BASE_PAGE_SIZE-1);
63        lpaddr_t offset = address & (BASE_PAGE_SIZE-1);
64        // XXX: should be address+size <= ...
65        // Need to add proper register size
66        if (address_base >= fid.base &&
67                (address_base + size) <= (fid.base + fid.bytes)) {
68            void* frame_base;
69            err = vspace_map_one_frame_attr(&frame_base, size,
70                                            device_cap_iter, VREGION_FLAGS_READ_WRITE_NOCACHE,
71                                            NULL, NULL);
72            *return_address = (lvaddr_t)frame_base + offset;
73            if (err_is_fail(err)) {
74                DEBUG_ERR(err, "Failure in vspace_map_one_frame_attr.\n");
75            }
76            return err;
77        }
78    }
79
80    return DRIVERKIT_ERR_NO_CAP_FOUND;
81}
82
83errval_t map_device_cap(struct capref device_cap, lvaddr_t *return_address) {
84    struct frame_identity fid;
85    errval_t err = frame_identify(device_cap, &fid);
86    if (err_is_fail(err)) {
87        DEBUG_ERR(err, "Failure in frame_identify");
88        return err;
89    }
90    return vspace_map_one_frame_attr((void**)return_address, fid.bytes,
91                                    device_cap, VREGION_FLAGS_READ_WRITE_NOCACHE,
92                                    NULL, NULL);
93}
94