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 <allocman/mspace/virtual_pool.h>
14#include <allocman/allocman.h>
15#include <allocman/util.h>
16#include <stdlib.h>
17#include <sel4/sel4.h>
18#include <sel4utils/mapping.h>
19#include <vka/kobject_t.h>
20#include <vspace/mapping.h>
21#include <string.h>
22
23/* This allocator deliberately does not use the vspace library to manage mappings to prevent
24 * circular dependencies between the vspace library and the allocator */
25
26static int _add_page(allocman_t *alloc, seL4_CPtr pd, void *vaddr)
27{
28    cspacepath_t frame_path;
29    seL4_Word frame_cookie;
30    int error;
31    error = allocman_cspace_alloc(alloc, &frame_path);
32    if (error) {
33        ZF_LOGV("Failed to allocate slot");
34        return error;
35    }
36    frame_cookie = allocman_utspace_alloc(alloc, seL4_PageBits, seL4_ARCH_4KPage, &frame_path, true, &error);
37    if (error) {
38        allocman_cspace_free(alloc, &frame_path);
39        ZF_LOGV("Failed to allocate frame");
40        return error;
41    }
42    while ((error = seL4_ARCH_Page_Map(frame_path.capPtr, pd, (seL4_Word) vaddr, seL4_AllRights,
43                    seL4_ARCH_Default_VMAttributes)) == seL4_FailedLookup) {
44        cspacepath_t path;
45        error = allocman_cspace_alloc(alloc, &path);
46        if (error) {
47            ZF_LOGV("Failed to allocate slot");
48            break;
49        }
50        seL4_Word failed_bits = seL4_MappingFailedLookupLevel();
51        vspace_map_obj_t obj;
52        error = vspace_get_map_obj(failed_bits, &obj);
53        assert(error == 0);
54
55        seL4_Word cookie = allocman_utspace_alloc(alloc, obj.size_bits, obj.type, &path, false, &error);
56        if (error) {
57            allocman_cspace_free(alloc, &path);
58            ZF_LOGV("Failed to allocate paging structure");
59            break;
60        }
61        error = vspace_map_obj(&obj, path.capPtr, pd, (seL4_Word) vaddr, seL4_ARCH_Default_VMAttributes);
62        if (error != seL4_NoError) {
63            allocman_utspace_free(alloc, cookie, seL4_PageTableBits);
64            allocman_cspace_free(alloc, &path);
65            break;
66        }
67    }
68    if (error != seL4_NoError) {
69        allocman_cspace_free(alloc, &frame_path);
70        allocman_utspace_free(alloc, frame_cookie, seL4_PageBits);
71        return error;
72    }
73    /* zero the memory in case we were allocated from a device range */
74    memset(vaddr, 0, PAGE_SIZE_4K);
75    return 0;
76}
77
78static k_r_malloc_header_t *_morecore(size_t cookie, mspace_k_r_malloc_t *k_r_malloc, size_t new_units)
79{
80    size_t new_size;
81    k_r_malloc_header_t *new_header;
82    mspace_virtual_pool_t *virtual_pool = (mspace_virtual_pool_t*)cookie;
83    new_size = new_units * sizeof(k_r_malloc_header_t);
84
85    if (virtual_pool->pool_ptr + new_size > virtual_pool->pool_limit) {
86        ZF_LOGV("morecore out of virtual pool");
87        return NULL;
88    }
89    while (virtual_pool->pool_ptr + new_size > virtual_pool->pool_top) {
90        int error;
91        error = _add_page(virtual_pool->morecore_alloc, virtual_pool->pd, virtual_pool->pool_top);
92        if (error) {
93            ZF_LOGV("morecore failed to add page");
94            return NULL;
95        }
96        virtual_pool->pool_top += PAGE_SIZE_4K;
97    }
98    new_header = (k_r_malloc_header_t*)virtual_pool->pool_ptr;
99    virtual_pool->pool_ptr += new_size;
100    return new_header;
101}
102
103void mspace_virtual_pool_create(mspace_virtual_pool_t *virtual_pool, struct mspace_virtual_pool_config config)
104{
105    virtual_pool->pool_ptr = config.vstart;
106    virtual_pool->pool_top = virtual_pool->pool_ptr;
107    virtual_pool->pool_limit = config.vstart + config.size;
108    virtual_pool->morecore_alloc = NULL;
109    virtual_pool->pd = config.pd;
110    mspace_k_r_malloc_init(&virtual_pool->k_r_malloc, (size_t)virtual_pool, _morecore);
111}
112
113void *_mspace_virtual_pool_alloc(struct allocman *alloc, void *_virtual_pool, size_t bytes, int *error)
114{
115    void *ret;
116    mspace_virtual_pool_t *virtual_pool = (mspace_virtual_pool_t*)_virtual_pool;
117    virtual_pool->morecore_alloc = alloc;
118    ret = mspace_k_r_malloc_alloc(&virtual_pool->k_r_malloc, bytes);
119    virtual_pool->morecore_alloc = NULL;
120    SET_ERROR(error, (ret == NULL) ? 1 : 0);
121    return ret;
122}
123
124void _mspace_virtual_pool_free(struct allocman *alloc, void *_virtual_pool, void *ptr, size_t bytes)
125{
126    mspace_virtual_pool_t *virtual_pool = (mspace_virtual_pool_t*)_virtual_pool;
127    virtual_pool->morecore_alloc = alloc;
128    mspace_k_r_malloc_free(&virtual_pool->k_r_malloc, ptr);
129    virtual_pool->morecore_alloc = NULL;
130}
131