1/**
2 * \file
3 */
4
5/*
6 * Copyright (c) 2009, 2010, 2011, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#include "vmkitmon.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <barrelfish/barrelfish.h>
20#include <barrelfish/nameservice_client.h>
21#include <barrelfish/cpu_arch.h>
22/* #include <barrelfish/terminal.h> */
23#include <vfs/vfs.h>
24#include <vfs/vfs_path.h>
25#include <spawndomain/spawndomain.h>
26#include <if/arrakis_defs.h>
27#include <if/monitor_blocking_defs.h>
28/* #include <timer/timer.h> */
29#include "ps.h"
30
31#define SERVICE_BASENAME    "arrakis" // the core ID is appended to this
32
33static errval_t spawn_arrakis(const char *path, char *const argv[], const char *argbuf,
34			      size_t argbytes, char *const envp[],
35			      struct capref inheritcn_cap, struct capref argcn_cap,
36			      domainid_t *domainid)
37{
38    errval_t err, msgerr;
39
40    /* read file into memory */
41    vfs_handle_t fh;
42    err = vfs_open(path, &fh);
43    if (err_is_fail(err)) {
44        return err_push(err, SPAWN_ERR_LOAD);
45    }
46
47    struct vfs_fileinfo info;
48    err = vfs_stat(fh, &info);
49    if (err_is_fail(err)) {
50        vfs_close(fh);
51        return err_push(err, SPAWN_ERR_LOAD);
52    }
53
54    assert(info.type == VFS_FILE);
55    uint8_t *image = malloc(info.size);
56    if (image == NULL) {
57        vfs_close(fh);
58        return err_push(err, SPAWN_ERR_LOAD);
59    }
60
61    size_t pos = 0, readlen;
62    do {
63        err = vfs_read(fh, &image[pos], info.size - pos, &readlen);
64        if (err_is_fail(err)) {
65            vfs_close(fh);
66            free(image);
67            return err_push(err, SPAWN_ERR_LOAD);
68        } else if (readlen == 0) {
69            vfs_close(fh);
70            free(image);
71            return SPAWN_ERR_LOAD; // XXX
72        } else {
73            pos += readlen;
74        }
75    } while (err_is_ok(err) && readlen > 0 && pos < info.size);
76
77    err = vfs_close(fh);
78    if (err_is_fail(err)) {
79        DEBUG_ERR(err, "failed to close file %s", path);
80    }
81
82    // find short name (last part of path)
83    const char *name = strrchr(path, VFS_PATH_SEP);
84    if (name == NULL) {
85        name = path;
86    } else {
87        name++;
88    }
89
90    /* spawn the image */
91    struct spawninfo si;
92    err = spawn_load_image(&si, (lvaddr_t)image, info.size, CURRENT_CPU_TYPE,
93                           name, disp_get_core_id(), argv, envp, inheritcn_cap,
94                           argcn_cap);
95    if (err_is_fail(err)) {
96        free(image);
97        return err;
98    }
99
100    free(image);
101
102    /* request connection from monitor */
103    struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
104    struct capref monep;
105    err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &msgerr, &monep);
106    if (err_is_fail(err)) {
107        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
108    } else if (err_is_fail(msgerr)) {
109        return msgerr;
110    }
111
112    /* copy connection into the new domain */
113    struct capref destep = {
114        .cnode = si.taskcn,
115        .slot  = TASKCN_SLOT_MONITOREP,
116    };
117    err = cap_copy(destep, monep);
118    if (err_is_fail(err)) {
119        spawn_free(&si);
120        cap_destroy(monep);
121        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
122    }
123
124    err = cap_destroy(monep);
125    if (err_is_fail(err)) {
126        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
127    }
128
129    debug_printf("spawning %s on core %u\n", path, disp_get_core_id());
130
131    /* give the perfmon capability */
132    struct capref dest, src;
133    dest.cnode = si.taskcn;
134    dest.slot = TASKCN_SLOT_PERF_MON;
135    src.cnode = cnode_task;
136    src.slot = TASKCN_SLOT_PERF_MON;
137    err = cap_copy(dest, src);
138    if (err_is_fail(err)) {
139        return err_push(err, INIT_ERR_COPY_PERF_MON);
140    }
141
142    // Create a new guest control structure
143    struct guest *g = guest_create();
144    assert(g != NULL);
145
146    // run the domain
147    spawn_guest_domain(g, &si);
148
149    err = guest_make_runnable(g, true);
150    assert_err(err, "guest_make_runnable");
151
152    // Allocate domain id
153    struct ps_entry *pe = malloc(sizeof(struct ps_entry));
154    assert(pe != NULL);
155    memset(pe, 0, sizeof(struct ps_entry));
156    memcpy(pe->argv, (CONST_CAST)argv, MAX_CMDLINE_ARGS*sizeof(*argv));
157    pe->argbuf = argbuf;
158    pe->argbytes = argbytes;
159    /*
160     * NB: It's important to keep a copy of the DCB *and* the root
161     * CNode around.  We need to revoke both (in the right order, see
162     * kill_domain() below), so that we ensure no one else is
163     * referring to the domain's CSpace anymore. Especially the loop
164     * created by placing rootcn into its own address space becomes a
165     * problem here.
166     */
167    err = slot_alloc(&pe->rootcn_cap);
168    assert(err_is_ok(err));
169    err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
170    pe->rootcn = si.rootcn;
171    assert(err_is_ok(err));
172    err = slot_alloc(&pe->dcb);
173    assert(err_is_ok(err));
174    err = cap_copy(pe->dcb, si.dcb);
175    assert(err_is_ok(err));
176    pe->status = PS_STATUS_RUNNING;
177    err = ps_allocate(pe, domainid);
178    if(err_is_fail(err)) {
179        free(pe);
180    }
181
182    // Store in target dispatcher frame
183    struct dispatcher_generic *dg = get_dispatcher_generic(si.handle);
184    dg->domain_id = *domainid;
185
186    /* cleanup */
187    err = spawn_free(&si);
188    if (err_is_fail(err)) {
189        return err_push(err, SPAWN_ERR_FREE);
190    }
191
192    return SYS_ERR_OK;
193}
194
195struct pending_spawn_response {
196    struct arrakis_binding *b;
197    errval_t err;
198    domainid_t domainid;
199};
200
201static void retry_spawn_domain_response(void *a)
202{
203    errval_t err;
204
205    struct pending_spawn_response *r = (struct pending_spawn_response*)a;
206    struct arrakis_binding *b = r->b;
207
208    err = b->tx_vtbl.spawn_arrakis_domain_response(b, NOP_CONT, r->err, r->domainid);
209
210    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
211        // try again
212        err = b->register_send(b, get_default_waitset(),
213                               MKCONT(retry_spawn_domain_response,a));
214    }
215    if (err_is_fail(err)) {
216        DEBUG_ERR(err, "error sending spawn_domain reply\n");
217    }
218
219    free(a);
220}
221
222
223static errval_t spawn_reply(struct arrakis_binding *b, errval_t rerr,
224                            domainid_t domainid)
225{
226    errval_t err;
227
228    err = b->tx_vtbl.spawn_arrakis_domain_response(b, NOP_CONT, rerr, domainid);
229
230    if (err_is_fail(err)) {
231        DEBUG_ERR(err, "error sending spawn_domain reply\n");
232
233        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
234            // this will be freed in the retry handler
235            struct pending_spawn_response *sr =
236                malloc(sizeof(struct pending_spawn_response));
237            if (sr == NULL) {
238                return LIB_ERR_MALLOC_FAIL;
239            }
240            sr->b = b;
241            sr->err = rerr;
242            sr->domainid = domainid;
243            err = b->register_send(b, get_default_waitset(),
244                                   MKCONT(retry_spawn_domain_response, sr));
245            if (err_is_fail(err)) {
246                // note that only one continuation may be registered at a time
247                free(sr);
248                DEBUG_ERR(err, "register_send failed!");
249                return err;
250            }
251        }
252    }
253
254    return SYS_ERR_OK;
255}
256
257
258static void spawn_with_caps_handler(struct arrakis_binding *b, const char *path,
259                                    const char *argbuf, size_t argbytes,
260                                    const char *envbuf, size_t envbytes,
261                                    struct capref inheritcn_cap,
262                                    struct capref argcn_cap)
263{
264    errval_t err;
265    domainid_t domainid = 0;
266
267    /* printf("arrakismon: spawning '%s'\n", path); */
268
269    /* extract arguments from buffer */
270    char * argv[MAX_CMDLINE_ARGS + 1];
271    int i = 0;
272    size_t pos = 0;
273    while (pos < argbytes && i < MAX_CMDLINE_ARGS) {
274        argv[i++] = (CONST_CAST)argbuf + pos;
275        char *end = memchr(&argbuf[pos], '\0', argbytes - pos);
276        if (end == NULL) {
277            err = SPAWN_ERR_GET_CMDLINE_ARGS;
278            goto finish;
279        }
280        pos = end - argbuf + 1;
281    }
282    assert(i <= MAX_CMDLINE_ARGS);
283    argv[i] = NULL;
284
285    /* extract environment from buffer */
286    char *envp[MAX_CMDLINE_ARGS + 1];
287    i = 0;
288    pos = 0;
289    while (pos < envbytes && i < MAX_CMDLINE_ARGS) {
290        envp[i++] = (CONST_CAST)envbuf + pos;
291        char *end = memchr(&envbuf[pos], '\0', envbytes - pos);
292        if (end == NULL) {
293            err = SPAWN_ERR_GET_CMDLINE_ARGS;
294            goto finish;
295        }
296        pos = end - envbuf + 1;
297    }
298    assert(i <= MAX_CMDLINE_ARGS);
299    envp[i] = NULL;
300
301    char *npath = alloca(strlen(path));
302    strcpy(npath, path);
303    vfs_path_normalise(npath);
304
305    err = spawn_arrakis(npath, argv, argbuf, argbytes, envp, inheritcn_cap,
306			argcn_cap, &domainid);
307    if (!capref_is_null(inheritcn_cap)) {
308        errval_t err2;
309        err2 = cap_delete(inheritcn_cap);
310        assert(err_is_ok(err2));
311    }
312    if (!capref_is_null(argcn_cap)) {
313        errval_t err2;
314        err2 = cap_delete(argcn_cap);
315        assert(err_is_ok(err2));
316    }
317
318 finish:
319    if(err_is_fail(err)) {
320        DEBUG_ERR(err, "spawn");
321    }
322
323    err = spawn_reply(b, err, domainid);
324
325    if (err_is_fail(err)) {
326        // not much we can do about this
327        DEBUG_ERR(err, "while sending reply in spawn_handler");
328    }
329}
330
331
332static void spawn_handler(struct arrakis_binding *b, const char *path, const char *argbuf,
333                          size_t argbytes, const char *envbuf, size_t envbytes)
334{
335    spawn_with_caps_handler(b, path, argbuf, argbytes, envbuf, envbytes,
336                            NULL_CAP, NULL_CAP);
337}
338
339static struct arrakis_rx_vtbl rx_vtbl = {
340    .spawn_arrakis_domain_call = spawn_handler,
341};
342
343static void export_cb(void *st, errval_t err, iref_t iref)
344{
345    if (err_is_fail(err)) {
346        USER_PANIC_ERR(err, "export failed");
347    }
348
349    // construct name
350    char namebuf[32];
351    size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
352                          disp_get_core_id());
353    assert(len < sizeof(namebuf));
354    namebuf[sizeof(namebuf) - 1] = '\0';
355
356    // register this iref with the name service
357    err = nameservice_register(namebuf, iref);
358    if (err_is_fail(err)) {
359        USER_PANIC_ERR(err, "nameservice_register failed");
360    }
361}
362
363static errval_t connect_cb(void *st, struct arrakis_binding *b)
364{
365    // copy my message receive handler vtable to the binding
366    b->rx_vtbl = rx_vtbl;
367    return SYS_ERR_OK;
368}
369
370static errval_t start_service(void)
371{
372    return arrakis_export(NULL, export_cb, connect_cb, get_default_waitset(),
373			  IDC_EXPORT_FLAGS_DEFAULT);
374}
375
376int main (int argc, char *argv[])
377{
378    vfs_init();
379
380    /* err = timer_init(); */
381    /* if (err_is_fail(err)) { */
382    /*     USER_PANIC_ERR(err, "error initialising timer client library\n"); */
383    /* } */
384
385#if 0
386    /* Initialization */
387    err = realmode_init();
388    assert_err(err, "realmode_init");
389
390    // aquire the standard input
391    err = terminal_want_stdin(TERMINAL_SOURCE_SERIAL);
392    assert_err(err, "terminal_want_stdin");
393#endif
394
395    // Start arrakis service
396    start_service();
397
398    // Spawn test arrakis application
399    /* err = spawn_arrakis("/x86_64/sbin/hellotest", ); */
400    /* assert_err(err, "spawn_arrakis"); */
401
402#if 0
403    guest = guest_create ();
404    assert(guest != NULL);
405    err = guest_make_runnable(guest, true);
406    assert_err(err, "guest_make_runnable");
407
408    printf("arrakismon: main loop\n");
409#endif
410
411    messages_handler_loop();
412}
413