1/**
2 * \file
3 * \brief Local memory allocator for init till mem_serv is ready to use
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2009, 2011, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include "init.h"
16#include <mm/mm.h>
17
18/* parameters for local memory allocator used until we spawn mem_serv */
19#define OBJBITS_DISPATCHER (10)
20// XXX: 16MB is not enough to create tracing buffers on babybel for all cores
21#define MM_REQUIREDBITS    27          ///< Required size of memory to boot (128MB)
22#define MM_REQUIREDBYTES   (1UL << MM_REQUIREDBITS)
23#define MM_MAXSIZEBITS     (MM_REQUIREDBITS + 3) ///< Max size of memory in allocator
24#define MM_MINSIZEBITS     BASE_PAGE_BITS ///< Min size of allocation
25#define MM_MAXCHILDBITS    1           ///< Max branching factor of BTree nodes
26#define MM_MAXDEPTH (MM_MAXSIZEBITS - MM_MINSIZEBITS + 1)   ///< BTree depth
27#define MM_NNODES   ((1UL << MM_MAXDEPTH) + MM_MINSIZEBITS - OBJBITS_DISPATCHER) ///< Max BTree nodes
28#define MM_NCNODES  DIVIDE_ROUND_UP(MM_NNODES, 1UL << DEFAULT_CNODE_BITS) //CNodes
29
30// Number of slots placed in smallcn of mem_serv
31#define MEM_SERV_SMALLCN_SLOTS 10
32
33/// MM allocator instance data
34static struct mm mymm;
35static bool huh = false;
36
37static errval_t mymm_alloc(struct capref *ret, uint8_t bits, uint64_t minbase,
38                           uint64_t maxlimit)
39{
40    if (huh) {
41        debug_printf("%s: Called self from %p\n", __FUNCTION__, __builtin_return_address(0));
42        while(true);
43    }
44    huh = true;
45    /* XXX: although we have calculated the space requirements for
46     * MM_MINSIZEBITS, we only ever allocate a single dispatcher from this
47     * allocator, so this should be safe */
48    assert(bits >= OBJBITS_DISPATCHER);
49    errval_t err = mm_alloc(&mymm, bits, ret, NULL);
50    huh = false;
51    return err;
52}
53
54/**
55 * \brief Setups a local memory allocator for init to use till the memory server
56 * is ready to be used.
57 */
58errval_t initialize_ram_alloc(void)
59{
60    errval_t err;
61
62    /* init slot allocator */
63    static struct slot_alloc_basecn init_slot_alloc;
64    err = slot_alloc_basecn_init(&init_slot_alloc);
65    if (err_is_fail(err)) {
66        return err_push(err, MM_ERR_SLOT_ALLOC_INIT);
67    }
68
69    /* walk bootinfo looking for suitable RAM cap to use
70     * we pick the first cap equal to MM_REQUIREDBITS,
71     * or else the next closest less than MM_MAXSIZEBITS */
72    int mem_slot = 0;
73    struct capref mem_cap = {
74        .cnode = cnode_super,
75        .slot = 0,
76    };
77
78    /* get destination slot for retype */
79    genpaddr_t region_base = 0;
80    struct capref region_for_init;
81    err = slot_alloc_basecn(&init_slot_alloc, 1, &region_for_init);
82    if (err_is_fail(err)) {
83        DEBUG_ERR(err, "slot_alloc_basecn in initialize_ram_alloc");
84        return err_push(err, MM_ERR_SLOT_NOSLOTS);
85    }
86
87    assert(bi != NULL);
88    for (int i = 0; i < bi->regions_length; i++) {
89        assert(!bi->regions[i].mr_consumed);
90        if (bi->regions[i].mr_type == RegionType_Empty) {
91            if (bi->regions[i].mr_bytes >= MM_REQUIREDBYTES) {
92                mem_cap.slot = mem_slot;
93                if (bi->regions[i].mr_bytes == MM_REQUIREDBYTES) {
94                    bi->regions[i].mr_consumed = true;
95                    region_base = bi->regions[i].mr_base;
96                    break;
97                }
98
99                /* found cap bigger than required; cut off end */
100                bi->regions[i].mr_bytes -= MM_REQUIREDBYTES;
101                // can use mr_bytes as offset here
102                err = cap_retype(region_for_init, mem_cap,
103                                 bi->regions[i].mr_bytes, ObjType_RAM,
104                                 MM_REQUIREDBYTES, 1);
105                if (err_is_fail(err)) {
106                    return err_push(err, MM_ERR_CHUNK_NODE);
107                }
108                mem_cap = region_for_init;
109                region_base = bi->regions[i].mr_base + bi->regions[i].mr_bytes;
110                break;
111            }
112            mem_slot++;
113        }
114    }
115
116    if (region_base == 0) {
117        USER_PANIC("Error: no RAM capability >= %zu MB found\n",
118                MM_REQUIREDBYTES / 1024 / 1024);
119    }
120
121    /*  init MM allocator */
122    err = mm_init(&mymm, ObjType_RAM, region_base,
123                  MM_REQUIREDBITS, MM_MAXCHILDBITS, NULL,
124                  slot_alloc_basecn, NULL, &init_slot_alloc, true);
125    if (err_is_fail(err)) {
126        return err_push(err, MM_ERR_MM_INIT);
127    }
128
129    /* give MM allocator enough static storage for its node allocator */
130    assert(1UL << OBJBITS_DISPATCHER == OBJSIZE_DISPATCHER);
131    static char nodebuf[SLAB_STATIC_SIZE(MM_NNODES, MM_NODE_SIZE(MM_MAXCHILDBITS))];
132    slab_grow(&mymm.slabs, nodebuf, sizeof(nodebuf));
133
134    /* add single RAM cap to allocator */
135    /* XXX: can't use mm_add_multi here, as the allocator tends to choke when
136     * we add smaller regions before larger */
137    debug_printf("using %#"PRIxGENPADDR", %zu MB for init's allocator\n",
138            region_base, MM_REQUIREDBYTES / 1024 / 1024);
139    err = mm_add(&mymm, mem_cap, MM_REQUIREDBITS, region_base);
140    if (err_is_fail(err)) {
141        return err_push(err, MM_ERR_MM_ADD);
142    }
143
144    // initialise generic RAM allocator to use local allocator
145    err = ram_alloc_set(mymm_alloc);
146    if (err_is_fail(err)) {
147        return err_push(err, LIB_ERR_RAM_ALLOC_SET);
148    }
149
150    return SYS_ERR_OK;
151}
152