71f0588234ef8ee5df51de4bd90a5c88b8bfd29d
[barrelfish] / usr / arrakismon / vmkitmon.c
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_rpcclient_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
33 static errval_t spawn_arrakis(char *path, char *const argv[], 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     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_rpc_client *mrpc = get_monitor_blocking_rpc_client();
104     struct capref monep;
105     err = mrpc->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, 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
195 struct pending_spawn_response {
196     struct arrakis_binding *b;
197     errval_t err;
198     domainid_t domainid;
199 };
200
201 static 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
223 static 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
258 static void spawn_with_caps_handler(struct arrakis_binding *b, char *path,
259                                     char *argbuf, size_t argbytes,
260                                     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++] = &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++] = &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     vfs_path_normalise(path);
302
303     err = spawn_arrakis(path, argv, argbuf, argbytes, envp, inheritcn_cap,
304                         argcn_cap, &domainid);
305     if (!capref_is_null(inheritcn_cap)) {
306         errval_t err2;
307         err2 = cap_delete(inheritcn_cap);
308         assert(err_is_ok(err2));
309     }
310     if (!capref_is_null(argcn_cap)) {
311         errval_t err2;
312         err2 = cap_delete(argcn_cap);
313         assert(err_is_ok(err2));
314     }
315
316  finish:
317     if(err_is_fail(err)) {
318         free(argbuf);
319         DEBUG_ERR(err, "spawn");
320     }
321
322     err = spawn_reply(b, err, domainid);
323
324     if (err_is_fail(err)) {
325         // not much we can do about this
326         DEBUG_ERR(err, "while sending reply in spawn_handler");
327     }
328
329     free(envbuf);
330     free(path);
331 }
332
333
334 static void spawn_handler(struct arrakis_binding *b, char *path, char *argbuf,
335                           size_t argbytes, char *envbuf, size_t envbytes)
336 {
337     spawn_with_caps_handler(b, path, argbuf, argbytes, envbuf, envbytes,
338                             NULL_CAP, NULL_CAP);
339 }
340
341 static struct arrakis_rx_vtbl rx_vtbl = {
342     .spawn_arrakis_domain_call = spawn_handler,
343 };
344
345 static void export_cb(void *st, errval_t err, iref_t iref)
346 {
347     if (err_is_fail(err)) {
348         USER_PANIC_ERR(err, "export failed");
349     }
350
351     // construct name
352     char namebuf[32];
353     size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
354                           disp_get_core_id());
355     assert(len < sizeof(namebuf));
356     namebuf[sizeof(namebuf) - 1] = '\0';
357
358     // register this iref with the name service
359     err = nameservice_register(namebuf, iref);
360     if (err_is_fail(err)) {
361         USER_PANIC_ERR(err, "nameservice_register failed");
362     }
363 }
364
365 static errval_t connect_cb(void *st, struct arrakis_binding *b)
366 {
367     // copy my message receive handler vtable to the binding
368     b->rx_vtbl = rx_vtbl;
369     return SYS_ERR_OK;
370 }
371
372 static errval_t start_service(void)
373 {
374     return arrakis_export(NULL, export_cb, connect_cb, get_default_waitset(),
375                           IDC_EXPORT_FLAGS_DEFAULT);
376 }
377
378 int main (int argc, char *argv[])
379 {
380     vfs_init();
381
382     /* err = timer_init(); */
383     /* if (err_is_fail(err)) { */
384     /*     USER_PANIC_ERR(err, "error initialising timer client library\n"); */
385     /* } */
386
387 #if 0
388     /* Initialization */
389     err = realmode_init();
390     assert_err(err, "realmode_init");
391
392     // aquire the standard input
393     err = terminal_want_stdin(TERMINAL_SOURCE_SERIAL);
394     assert_err(err, "terminal_want_stdin");
395 #endif
396
397     // Start arrakis service
398     start_service();
399
400     // Spawn test arrakis application
401     /* err = spawn_arrakis("/x86_64/sbin/hellotest", ); */
402     /* assert_err(err, "spawn_arrakis"); */
403
404 #if 0
405     guest = guest_create ();
406     assert(guest != NULL);
407     err = guest_make_runnable(guest, true);
408     assert_err(err, "guest_make_runnable");
409
410     printf("arrakismon: main loop\n");
411 #endif
412
413     messages_handler_loop();
414 }