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 <vspace/vspace.h>
15#include <utils/page.h>
16
17void *vspace_new_sized_stack(vspace_t *vspace, size_t n_pages)
18{
19    int error = 0;
20    void *vaddr = NULL;
21
22    /* one extra page for the guard */
23    reservation_t reserve = vspace_reserve_range(vspace, (n_pages + 1) * PAGE_SIZE_4K,
24                                                 seL4_AllRights, 1, &vaddr);
25
26    if (reserve.res == NULL) {
27        return NULL;
28    }
29
30    /* reserve the first page as the guard */
31    uintptr_t stack_bottom = (uintptr_t) vaddr + PAGE_SIZE_4K;
32
33    /* create and map the pages */
34    error = vspace_new_pages_at_vaddr(vspace, (void *) stack_bottom, n_pages, seL4_PageBits, reserve);
35
36    if (error) {
37        vspace_free_reservation(vspace, reserve);
38        return NULL;
39    }
40
41    return (void *)(stack_bottom + (n_pages * PAGE_SIZE_4K));
42}
43
44void vspace_free_sized_stack(vspace_t *vspace, void *stack_top, size_t n_pages)
45{
46    if (n_pages > 0) {
47        uintptr_t stack_bottom = (uintptr_t) stack_top - (n_pages * PAGE_SIZE_4K);
48        vspace_unmap_pages(vspace, (void *) stack_bottom, n_pages,
49                           seL4_PageBits, (vka_t *) VSPACE_FREE);
50        vspace_free_reservation_by_vaddr(vspace, (void *)(stack_bottom - PAGE_SIZE_4K));
51    } else {
52        ZF_LOGW("Freeing 0 sized stack");
53    }
54}
55
56void *vspace_new_ipc_buffer(vspace_t *vspace, seL4_CPtr *page)
57{
58
59    void *vaddr = vspace_new_pages(vspace, seL4_AllRights, 1, seL4_PageBits);
60    if (vaddr == NULL) {
61        return NULL;
62    }
63
64    *page = vspace_get_cap(vspace, vaddr);
65
66    return vaddr;
67
68}
69
70void vspace_free_ipc_buffer(vspace_t *vspace, void *addr)
71{
72    vspace_unmap_pages(vspace, addr, 1, seL4_PageBits, VSPACE_FREE);
73}
74
75void *vspace_share_mem(vspace_t *from, vspace_t *to, void *start, int num_pages, size_t size_bits,
76                       seL4_CapRights_t rights, int cacheable)
77{
78    void *result;
79
80    /* reserve a range to map the shared memory in to */
81    reservation_t res = vspace_reserve_range_aligned(to, num_pages * (BIT(size_bits)), size_bits,
82                                                     rights, cacheable, &result);
83
84    if (res.res == NULL) {
85        ZF_LOGE("Failed to reserve range");
86        return NULL;
87    }
88
89    int error = vspace_share_mem_at_vaddr(from, to, start, num_pages, size_bits, result, res);
90    if (error) {
91        /* fail */
92        result = NULL;
93    }
94
95    /* clean up reservation */
96    vspace_free_reservation(to, res);
97
98    return result;
99}
100
101int vspace_access_page_with_callback(vspace_t *from, vspace_t *to, void *access_addr, size_t size_bits,
102                                     seL4_CapRights_t rights, int cacheable, vspace_access_callback_fn callback, void *cookie)
103{
104    void *to_vaddr;
105    to_vaddr = vspace_share_mem(from, to, access_addr, 1, size_bits, rights, cacheable);
106    if (to_vaddr == NULL) {
107        ZF_LOGE("Failed to access page");
108        return -1;
109    }
110
111    /* Invoke callback with mapped address */
112    int res = callback(access_addr, to_vaddr, cookie);
113
114    /* Remove mappings from destination vspace */
115    vspace_unmap_pages(to, to_vaddr, 1, size_bits, (vka_t *) VSPACE_FREE);
116
117    return res;
118}
119
120void *vspace_new_pages_with_config(vspace_t *vspace, vspace_new_pages_config_t *config, seL4_CapRights_t rights)
121{
122    reservation_t res;
123    if (config->vaddr == NULL) {
124        res = vspace_reserve_range_aligned(vspace, config->num_pages * SIZE_BITS_TO_BYTES(config->size_bits),
125                                           config->size_bits,
126                                           rights, true, &config->vaddr);
127    } else {
128        res =  vspace_reserve_range_at(vspace, config->vaddr,
129                                       config->num_pages * SIZE_BITS_TO_BYTES(config->size_bits),
130                                       rights, true);
131    }
132    if (res.res == NULL) {
133        ZF_LOGE("Failed to reserve range");
134        return NULL;
135    }
136
137    UNUSED int error = vspace_new_pages_at_vaddr_with_config(vspace, config, res);
138    vspace_free_reservation(vspace, res);
139
140    if (error) {
141        return NULL;
142    }
143
144    return config->vaddr;
145}
146
147/* this function is for backwards compatibility after interface change */
148reservation_t vspace_reserve_range(vspace_t *vspace, size_t bytes,
149                                   seL4_CapRights_t rights, int cacheable, void **vaddr)
150{
151    return vspace_reserve_range_aligned(vspace, bytes, seL4_PageBits, rights, cacheable, vaddr);
152}
153