1/**
2 * \file
3 * \brief functionality to spawn domains
4 */
5
6/*
7 * Copyright (c) 2014 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, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <string.h>
17
18#include <barrelfish/barrelfish.h>
19#include <barrelfish/dispatch.h> // for disp_name()
20#include <spawndomain/spawndomain.h>
21
22#include <if/octopus_defs.h>
23#include <if/octopus_defs.h>
24#include <if/monitor_defs.h>
25#include <octopus/getset.h> // for oct_read TODO
26#include <octopus/trigger.h> // for NOP_TRIGGER
27
28#include "spawn.h"
29
30struct symval {
31    char *name;
32    genvaddr_t addr;
33};
34
35uint32_t symval_count;
36
37struct symval *symvals;
38
39/**
40 * \brief obtains the number of OpenMP symbols of the ELF file
41 *
42 * \param ret_count     returns the number of symbols
43 *
44 * \returns SYS_ERR_OK on success
45 *          errval on failure
46 */
47errval_t spawn_symval_count(uint32_t *ret_count)
48{
49    if (!symvals) {
50        errval_t err;
51
52        err = spawn_symval_cache_init(1);
53        if (err_is_fail(err)) {
54            return err;
55        }
56    }
57
58    if (ret_count) {
59        *ret_count = symval_count;
60    }
61    return SYS_ERR_OK;
62}
63
64/**
65 * \brief initializes the symbol value cache for faster lookups
66 *
67 * \param lazy   do a lazy initialization i.e. only allocate memory for the
68 *               symbols but do not load them
69 *
70 * \returns SYS_ERR_OK on success
71 *          errval on error
72 */
73errval_t spawn_symval_cache_init(uint8_t lazy)
74{
75    errval_t err;
76
77    genvaddr_t count = 0;
78    err = spawn_symval_lookup_idx(0, NULL, &count);
79    if (err_is_fail(err)) {
80        return err;
81    }
82
83    symval_count = count;
84
85    if (count > 0) {
86        symvals = calloc(count + 1, sizeof(struct symval));
87        if (symvals == NULL) {
88            return LIB_ERR_MALLOC_FAIL;
89        }
90    }
91
92    if (lazy) {
93        return SYS_ERR_OK;
94    }
95
96    for (uint32_t i = 1; i <= count; ++i) {
97        err = spawn_symval_lookup_idx(i, &symvals[i].name, &symvals[i].addr);
98        if (err_is_fail(err)) {
99            return err;
100        }
101    }
102
103    return SYS_ERR_OK;
104}
105
106/**
107 * \brief looks up the symbol based on its name and adds it to the cache
108 *
109 * \param name      the name of the symbol to query
110 * \param ret_idx   returns the symbol index
111 * \param ret_addr  returns the address of the symbol
112 *
113 * \returns SYS_ERR_OK on success
114 *          errval on failure
115 */
116errval_t spawn_symval_lookup_name(char *name,
117                                  uint32_t *ret_idx,
118                                  genvaddr_t *ret_addr)
119{
120    errval_t err;
121
122    if (!symvals) {
123        err = spawn_symval_cache_init(1);
124        if (err_is_fail(err)) {
125            return err;
126        }
127    }
128
129    for (uint32_t i = 1; i <= symval_count; ++i) {
130        if (symvals[i].addr == 0) {
131            err = spawn_symval_lookup_idx(i, &symvals[i].name, &symvals[i].addr);
132            if (err_is_fail(err)) {
133                return err;
134            }
135        }
136        if (strcmp(name, symvals[i].name) == 0) {
137            if (ret_idx) {
138                *ret_idx = i;
139            }
140            if (ret_addr) {
141                *ret_addr = symvals[i].addr;
142            }
143            return SYS_ERR_OK;
144        }
145    }
146    // todo: errval
147    return -1;
148}
149
150/**
151 * \brief looks up the symbol information based on its address
152 *
153 * \param addr      the address to lookup
154 * \param ret_idx   returns the symbol index
155 * \param ret_name  returns the symbol name
156 *
157 * \returns SYS_ERR_OK on success
158 *          errval on error
159 */
160errval_t spawn_symval_lookup_addr(genvaddr_t addr,
161                                  uint32_t *ret_idx,
162                                  char **ret_name)
163{
164    errval_t err;
165
166    if (!symvals) {
167        err = spawn_symval_cache_init(1);
168        if (err_is_fail(err)) {
169            return err;
170        }
171    }
172
173    for (uint32_t i = 1; i <= symval_count; ++i) {
174        if (symvals[i].addr == 0) {
175            err = spawn_symval_lookup_idx(i, &symvals[i].name, &symvals[i].addr);
176            if (err_is_fail(err)) {
177                return err;
178            }
179        }
180        if (symvals[i].addr == addr) {
181            if (ret_idx) {
182                *ret_idx = i;
183            }
184            if (ret_name) {
185                *ret_name = symvals[i].name;
186            }
187
188            return SYS_ERR_OK;
189        }
190    }
191
192    // TODO: errval
193    return -1;
194}
195
196/**
197 * \brief looks up the symbol by a given index
198 *
199 * \param idx       the index of the symbol to look up
200 * \param ret_name  returns the name of the symbol
201 * \param ret_addr  returns the address of the symbol
202 *
203 * \returns SYS_ERR_OK on success
204 *          errval on failure
205 */
206errval_t spawn_symval_lookup_idx(uint32_t idx,
207                                 char **ret_name,
208                                 genvaddr_t *ret_addr)
209{
210    if (symvals) {
211        if (symvals[idx].addr != 0) {
212            if (ret_name) {
213                *ret_name = symvals[idx].name;
214            }
215            if (ret_addr) {
216                *ret_addr = symvals[idx].addr;
217            }
218        }
219    }
220
221    return spawn_symval_lookup(disp_name(), idx, ret_name, ret_addr);
222}
223
224/**
225 * \brief executes a lookup query on octopus to obtain the symbol
226 *
227 * \param binary    name of the binary to query
228 * \param idx       index of the symbol to query
229 * \param ret_name  returns the name of the symbol
230 * \param ret_addr  returns the address of the symbol
231 *
232 * \return
233 */
234errval_t spawn_symval_lookup(const char *binary,
235                             uint32_t idx,
236                             char **ret_name,
237                             genvaddr_t *ret_addr)
238{
239    errval_t err;
240
241    size_t len;
242
243    len = snprintf(NULL, 0, "%s.omp.%"PRIu32, binary, idx);
244    char *omp_entry = malloc(len+1);
245    if (omp_entry == NULL) {
246        return LIB_ERR_MALLOC_FAIL;
247    }
248    snprintf(omp_entry, len+1, "%s.omp.%"PRIu32, binary, idx);
249
250    struct octopus_binding *r = get_octopus_binding();
251    if (r == NULL) {
252        return LIB_ERR_NAMESERVICE_NOT_BOUND;
253    }
254
255    // transform to lower case
256    for (int i = 0; i < len; ++i) {
257        if (omp_entry[i] >= 'A' && omp_entry[i] <= 'Z') {
258            omp_entry[i] -= ('A' - 'a');
259        }
260    }
261
262    struct octopus_get_response__rx_args reply;
263    err = r->rpc_tx_vtbl.get(r, omp_entry, NOP_TRIGGER,
264                      reply.output, &reply.tid, &reply.error_code);
265    if (err_is_fail(err)) {
266        goto out;
267    }
268    err = reply.error_code;
269    if (err_is_fail(err)) {
270        if (err_no(err) == OCT_ERR_NO_RECORD) {
271            err = err_push(err, LIB_ERR_NAMESERVICE_UNKNOWN_NAME);
272        }
273        goto out;
274    }
275
276    uint64_t addr = 0;
277    char *symname = NULL;
278    err = oct_read(reply.output, "_ { sym: %s, addr: %d }", &symname, &addr);
279    if (err_is_fail(err) || symname == NULL) {
280        err = err_push(err, LIB_ERR_NAMESERVICE_INVALID_NAME);
281        goto out;
282    }
283    if (ret_addr != NULL) {
284        *ret_addr = addr;
285    }
286    if (ret_name != NULL) {
287        *ret_name = strdup(symname);
288    }
289
290    out:
291    free(omp_entry);
292    return err;
293}
294
295/**
296 * \brief registers a found symbol with octopus for later retrieval
297 *
298 * \param binary    the name of the binary
299 * \param idx       index of the symbol to insert
300 * \param symname   name of the symbol to insert
301 * \param address   address of the sybol to insert
302 *
303 * \returns SYS_ERR_OK on success
304 *          errval on error
305 */
306errval_t spawn_symval_register(const char *binary,
307                               uint32_t idx,
308                               const char *symname,
309                               genvaddr_t address)
310{
311
312    errval_t err = SYS_ERR_OK;
313
314    struct octopus_binding *r = get_octopus_binding();
315    if (r == NULL) {
316        return LIB_ERR_NAMESERVICE_NOT_BOUND;
317    }
318
319    if (symname[0] == '_') {
320        symname++;
321    }
322    // Format record
323    static const char* format = "%s.omp.%u { sym: %s, addr: %d }";
324    size_t len = snprintf(NULL, 0, format, binary, idx, symname, address);
325    char* record = malloc(len + 1);
326    if (record == NULL) {
327        return LIB_ERR_MALLOC_FAIL;
328    }
329    snprintf(record, len + 1, format, binary, idx, symname, address);
330    // transform to lower case
331    for (int i = 0; i < len; ++i) {
332        if (record[i] >= 'A' && record[i] <= 'Z') {
333            record[i] -= ('A' - 'a');
334        }
335    }
336
337    octopus_trigger_id_t tid;
338    errval_t error_code;
339    err = r->rpc_tx_vtbl.set(r, record, 0, NOP_TRIGGER,
340                      0, NULL, &tid, &error_code);
341    if (err_is_fail(err)) {
342        goto out;
343    }
344    err = error_code;
345
346out:
347    free(record);
348    return err;
349}
350
351