1/**
2 * \file
3 * \brief
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#define _USE_XOPEN /* for strdup() in string.h */
16
17#include <monitor.h>
18#include <barrelfish/cpu_arch.h>
19
20extern char **environ;
21
22/**
23 * \brief Some domains require special caps for functionality
24 * and we do not have a better way to to this yet
25 */
26static errval_t set_special_caps(struct spawninfo *si, const char *pname)
27{
28    struct capref src, dest;
29    errval_t err;
30
31    // lop off leading path for name comparison
32    const char *name = strrchr(pname, '/');
33    if (name == NULL) {
34        name = pname;
35    } else {
36        name++;
37    }
38
39    /* Pass IRQ cap to bfscope (XXX: kludge) */
40    if (!strcmp(name, "bfscope")) {
41        dest.cnode = si->taskcn;
42        dest.slot  = TASKCN_SLOT_IRQ;
43        src.cnode = cnode_task;
44        src.slot  = TASKCN_SLOT_IRQ;
45        err = cap_copy(dest, src);
46        if (err_is_fail(err)) {
47            return err_push(err, SPAWN_ERR_COPY_IRQ_CAP);
48        }
49    }
50
51    if (!strcmp(name, "kaluga")) {
52        src = cap_kernel;
53        dest.cnode = si->taskcn,
54        dest.slot  = TASKCN_SLOT_KERNELCAP;
55        err = cap_copy(dest, src);
56        if (err_is_fail(err)) {
57            DEBUG_ERR(err, "Can not give kernel cap to kaluga");
58            return err_push(err, SPAWN_ERR_COPY_KERNEL_CAP);
59        }
60	}
61
62    if (!strcmp(name, "proc_mgmt")) {
63        // Pass ProcessManager cap.
64        dest.cnode = si->taskcn;
65        dest.slot = TASKCN_SLOT_PROC_MNG;
66        src = cap_procmng;
67        err = cap_copy(dest, src);
68        if (err_is_fail(err)) {
69            DEBUG_ERR(err, "Can not give ProcessManager cap");
70            return err_push(err, SPAWN_ERR_COPY_PROC_MNG_CAP);
71        }
72    }
73
74#ifdef __k1om__
75    if (!strcmp(name, "xeon_phi")) {
76        dest.cnode = si->taskcn;
77        dest.slot  = TASKCN_SLOT_IO;
78        src.cnode = cnode_task;
79        src.slot  = TASKCN_SLOT_IO;
80        err = cap_copy(dest, src);
81        if (err_is_fail(err)) {
82            return err_push(err, SPAWN_ERR_COPY_IRQ_CAP);
83        }
84
85        dest.cnode = si->taskcn;
86        dest.slot  = TASKCN_SLOT_SYSMEM;
87        src.cnode = cnode_task;
88        src.slot  = TASKCN_SLOT_SYSMEM;
89        err = cap_copy(dest, src);
90        if (err_is_fail(err)) {
91            return err_push(err, SPAWN_ERR_COPY_IRQ_CAP);
92        }
93        dest.cnode = si->taskcn;
94        dest.slot  = TASKCN_SLOT_COREBOOT;
95        src.cnode = cnode_task;
96        src.slot  = TASKCN_SLOT_COREBOOT;
97        err = cap_copy(dest, src);
98        if (err_is_fail(err)) {
99            return err_push(err, SPAWN_ERR_COPY_IRQ_CAP);
100        }
101    }
102#endif
103
104    return SYS_ERR_OK;
105}
106
107/**
108 * \brief Spawn the domain identified by #name
109 */
110errval_t spawn_domain(char *name)
111{
112    errval_t err;
113
114    /* Get the module from the multiboot */
115    struct mem_region *module = multiboot_find_module(bi, name);
116    if (module == NULL) {
117        return SPAWN_ERR_FIND_MODULE;
118    }
119
120    /* Get and tokenize cmdline args */
121    char *args;
122    err = spawn_get_cmdline_args(module, &args);
123    if (err_is_fail(err)) {
124        return err_push(err, SPAWN_ERR_GET_CMDLINE_ARGS);
125    }
126    char *argv[MAX_CMDLINE_ARGS + 1];
127    spawn_tokenize_cmdargs(args, argv, ARRAY_LENGTH(argv));
128
129    err = spawn_module_with_args(name, module, argv, environ);
130    free(args);
131    return err;
132}
133/**
134 * \brief Spawn the domain identified by #name with the provided args
135 */
136errval_t spawn_domain_with_args(const char *name,
137                                char *const argv[], char *const envp[])
138{
139    struct mem_region *module = multiboot_find_module(bi, name);
140    if (module == NULL) {
141        return SPAWN_ERR_FIND_MODULE;
142    }
143
144    return spawn_module_with_args(name, module, argv, envp);
145}
146
147/**
148 * \brief Spawn the domain identified by #module with the provided args
149 */
150errval_t spawn_module_with_args(const char *name, struct mem_region *module,
151                                char *const argv[], char *const envp[])
152{
153    errval_t err;
154    struct spawninfo si;
155
156    printf("Spawning %s on core %d\n", name, my_core_id);
157
158    err = spawn_load_with_args(&si, module, name, my_core_id, argv, envp);
159    if (err_is_fail(err)) {
160        return err_push(err, SPAWN_ERR_LOAD);
161    }
162
163    // Set special caps
164    err = set_special_caps(&si, name);
165    if (err_is_fail(err)) {
166        return err_push(err, SPAWN_ERR_SET_CAPS);
167    }
168
169    // Set connection
170    err = monitor_client_setup(&si);
171    if (err_is_fail(err)) {
172        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
173    }
174
175    // Make runnable
176    err = spawn_run(&si);
177    if (err_is_fail(err)) {
178        return err_push(err, SPAWN_ERR_RUN);
179    }
180
181    // Cleanup
182    err = spawn_free(&si);
183    if (err_is_fail(err)) {
184        return err_push(err, SPAWN_ERR_FREE);
185    }
186
187    return SYS_ERR_OK;
188}
189
190/**
191 * \brief Spawn the domain in the given image with the provided args
192 */
193static errval_t spawn_image_with_args(const char *name, void *image,
194                                      size_t imagelen,
195                                      char *const argv[], char *const envp[])
196{
197    errval_t err;
198    struct spawninfo si;
199
200    printf("Spawning %s on core %d\n", name, my_core_id);
201
202    err = spawn_load_image(&si, (lvaddr_t)image, imagelen, CURRENT_CPU_TYPE,
203                           name, my_core_id, argv, envp, NULL_CAP, NULL_CAP);
204    if (err_is_fail(err)) {
205        return err_push(err, SPAWN_ERR_LOAD);
206    }
207
208    // Set special caps
209    err = set_special_caps(&si, name);
210    if (err_is_fail(err)) {
211        return err_push(err, SPAWN_ERR_SET_CAPS);
212    }
213
214    // Set connection
215    err = monitor_client_setup(&si);
216    if (err_is_fail(err)) {
217        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
218    }
219
220    // Make runnable
221    err = spawn_run(&si);
222    if (err_is_fail(err)) {
223        return err_push(err, SPAWN_ERR_RUN);
224    }
225
226    // Cleanup
227    err = spawn_free(&si);
228    if (err_is_fail(err)) {
229        return err_push(err, SPAWN_ERR_FREE);
230    }
231
232    return SYS_ERR_OK;
233}
234
235/**
236 * \brief Spawn all 'boot' domains in modules (menu.lst)
237 */
238errval_t spawn_all_domains(void)
239{
240    errval_t err;
241
242    char name[128];
243
244    for(size_t i = 0; i < bi->regions_length; i++) {
245        // Lookup modules in bootinfo
246        struct mem_region *region = &bi->regions[i];
247        if (region->mr_type != RegionType_Module) { /* Not of module type */
248            continue;
249        }
250
251        const char *mm_name = multiboot_module_name(region);
252        // Copy name because walking the multiboot changes the name field
253        strncpy(name, mm_name, sizeof(name));
254
255        // Lop off the path from the name
256        const char *short_name = strrchr(name, '/');
257        if (!short_name) {
258            short_name = name;
259        } else {
260            short_name++;
261        }
262
263        /* Do not spawn special domains */
264        if(!strcmp(short_name, "init")
265           || !strcmp(short_name, "skb")
266           || !strcmp(short_name, "ramfsd")
267           || !strcmp(short_name, "cpu")
268           || !strcmp(short_name, "monitor")
269           || !strcmp(short_name, "mem_serv")
270           || !strcmp(short_name, "xeon_phi")
271           || !strcmp(short_name, "proc_mgmt")
272          )
273        {
274            continue;
275        }
276
277        /* Get and tokenize cmdline args */
278        char *args;
279        err = spawn_get_cmdline_args(region, &args);
280        if (err_is_fail(err)) {
281            return err_push(err, SPAWN_ERR_GET_CMDLINE_ARGS);
282        }
283
284        // Pass the local arch-specific core ID to the PCI and spawnd domains
285        if(strcmp(short_name, "pci") == 0
286           || strcmp(short_name, "spawnd") == 0
287           || strcmp(short_name, "kaluga") == 0
288           || strcmp(short_name, "acpi") == 0
289           || strcmp(short_name, "ioapic") == 0) {
290            // Get hardware core ID
291            uintptr_t my_arch_id = 0;
292            err = invoke_monitor_get_arch_id(&my_arch_id);
293            assert(err_is_ok(err));
294
295            char *myargs = malloc(strlen(args) + 50);
296            snprintf(myargs, strlen(args) + 50, "%s apicid=%" PRIuPTR,
297                     args, my_arch_id);
298            free(args);
299            args = myargs;
300        }
301
302        char *argv[MAX_CMDLINE_ARGS + 1];
303        int argc = spawn_tokenize_cmdargs(args, argv, ARRAY_LENGTH(argv));
304
305        /* Only spawn boot time modules */
306        if (argc > 1 && strcmp(argv[1], "boot") == 0) {
307            err = spawn_module_with_args(name, region, argv, environ);
308            if (err_is_fail(err)) {
309                return err_push(err, MON_ERR_SPAWN_DOMAIN);
310            }
311        }
312        free(args);
313    }
314
315    return SYS_ERR_OK;
316}
317
318static struct {
319    genpaddr_t base;
320    size_t bytes;
321} spawnd_image;
322
323static void spawnd_image_reply_handler(struct intermon_binding *b,
324                                       genpaddr_t base, uint32_t bytes)
325{
326    assert(spawnd_image.base == 0);
327    assert(base > 0 && bytes > 0);
328    spawnd_image.base = base;
329    spawnd_image.bytes = bytes;
330}
331
332/**
333 * \brief Span a domain to this core.
334 */
335errval_t spawn_spawnd(struct intermon_binding *b)
336{
337    assert(!bsp_monitor);
338
339    // find out where the image is
340    b->rx_vtbl.spawnd_image_reply = spawnd_image_reply_handler;
341    errval_t err = b->tx_vtbl.spawnd_image_request(b, NOP_CONT);
342    if (err_is_fail(err)) {
343        return err_push(err, MON_ERR_SEND_REMOTE_MSG);
344    }
345
346    while(spawnd_image.base == 0) {
347        messages_wait_and_handle_next();
348    }
349
350    // construct cap to it
351    struct capability cap_raw = {
352        .type = ObjType_Frame,
353        .rights = CAPRIGHTS_ALLRIGHTS,
354        .u.frame = {
355            .base = spawnd_image.base,
356            .bytes = ROUND_UP(spawnd_image.bytes, BASE_PAGE_SIZE),
357        }
358    };
359    struct capref frame;
360    err = slot_alloc(&frame);
361    if (err_is_fail(err)) {
362        USER_PANIC_ERR(err, "allocating slot failed for spawnd image");
363    }
364
365    err = monitor_cap_create(frame, &cap_raw, my_core_id);
366    if (err_is_fail(err)) {
367        USER_PANIC_ERR(err, "monitor_cap_create failed");
368    }
369
370    // map the image in
371    // XXX: leak memobj/region
372    void *image;
373    err = vspace_map_one_frame(&image, spawnd_image.bytes, frame, NULL, NULL);
374    if (err_is_fail(err)) {
375        return err_push(err, LIB_ERR_VSPACE_MAP);
376    }
377
378    // spawn!
379    char *argv[] = { "spawnd", NULL };
380    return spawn_image_with_args(argv[0], image, spawnd_image.bytes, argv,
381                                 environ);
382}
383
384/**
385 * \brief Span a domain to this core.
386 */
387errval_t span_domain(struct capref vroot, struct capref dispframe)
388{
389    struct spawninfo si;
390    errval_t err;
391
392    memset(&si, 0, sizeof(si));
393
394    printf("Spanning domain to core %d\n", my_core_id);
395
396    // Span domain
397    err = spawn_span_domain(&si, vroot, dispframe);
398    if (err_is_fail(err)) {
399        return err_push(err, SPAWN_ERR_SPAN);
400    }
401
402    // Set connection
403    err = monitor_client_setup(&si);
404    if (err_is_fail(err)) {
405        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
406    }
407
408    // Make runnable
409    err = spawn_run(&si);
410    if (err_is_fail(err)) {
411        return err_push(err, SPAWN_ERR_RUN);
412    }
413
414    // Cleanup
415    err = spawn_free(&si);
416    if (err_is_fail(err)) {
417        return err_push(err, SPAWN_ERR_FREE);
418    }
419
420    return SYS_ERR_OK;
421}
422