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