1/** 2 * \file 3 * \brief ramfs service 4 */ 5 6/* 7 * Copyright (c) 2010, 2011, 2013, 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() 16#include <stdio.h> 17#include <string.h> 18#include <zlib.h> 19#include <barrelfish/barrelfish.h> 20#include <barrelfish/nameservice_client.h> 21#include <if/monitor_defs.h> 22#include <if/monitor_blocking_defs.h> 23#include <if/trivfs_defs.h> 24#include <spawndomain/spawndomain.h> 25#include "ramfs.h" 26#include <cpiobin.h> 27 28#define BOOTSCRIPT_FILE_NAME "bootmodules" 29 30static errval_t write_directory(struct dirent *root, const char *path); 31static errval_t write_file(struct dirent *root, const char *path, uint8_t *data, 32 size_t len); 33 34/* -------------------- CODE TO UNPACK RAMFS IMAGES ------------------------ */ 35 36static int cpio_entry_handler(int n, const cpio_generic_header_t *h, void *arg) 37{ 38 struct dirent *root = arg; 39 errval_t err; 40 41 if(CPIO_MODE_FILE == (h->mode & CPIO_MODE_FILE_TYPE_MASK)) { 42 err = write_file(root, h->name, (void *)h->data, h->datasize); 43 } else if(CPIO_MODE_DIRECTORY == (h->mode & CPIO_MODE_FILE_TYPE_MASK)) { 44 err = write_directory(root, h->name); 45 } else { 46 return 0; 47 } 48 49 if (err_is_fail(err)) { 50 USER_PANIC_ERR(err, "error writing file to ramfs"); 51 } 52 53 return 0; 54} 55 56static errval_t unpack_cpio(struct dirent *root, void *data, size_t len) 57{ 58 if (!cpio_archive_valid(data, len)) { 59 USER_PANIC("invalid CPIO archive"); 60 } 61 62 cpio_generic_header_t h; 63 cpio_visit(data, len, cpio_entry_handler, &h, root); 64 return SYS_ERR_OK; 65} 66 67static errval_t unpack_cpiogz(struct dirent *root, void *data, size_t len) 68{ 69 /* decompress the image to a local buffer */ 70 static uint8_t chunk[16384]; 71 z_stream d_stream; /* decompression stream */ 72 errval_t err; 73 int zerr; 74 75 size_t outbuflen = 16384, outbufpos = 0; 76 uint8_t *outbuf = malloc(outbuflen); 77 assert(outbuf != NULL); 78 79 memset(&d_stream, 0, sizeof(d_stream)); 80 d_stream.next_in = data; 81 d_stream.avail_in = len; 82 83 zerr = inflateInit2(&d_stream, MAX_WBITS + 32); 84 assert(zerr == Z_OK); 85 86 do { 87 do { 88 d_stream.avail_out = sizeof(chunk); 89 d_stream.next_out = chunk; 90 91 zerr = inflate(&d_stream, Z_NO_FLUSH); 92 if (zerr < 0 || zerr == Z_NEED_DICT) { 93 debug_printf("zlib error %d: %s\n", zerr, d_stream.msg); 94 abort(); // FIXME 95 } 96 97 size_t outchunklen = sizeof(chunk) - d_stream.avail_out; 98 while (outchunklen + outbufpos > outbuflen) { 99 // XXX: inefficient: grow output buffer, copying everything 100 outbuflen *= 2; 101 outbuf = realloc(outbuf, outbuflen); 102 assert(outbuf != NULL); 103 } 104 memcpy(&outbuf[outbufpos], chunk, outchunklen); 105 outbufpos += outchunklen; 106 } while(d_stream.avail_out == 0); 107 assert(zerr == Z_STREAM_END); 108 } while (zerr != Z_STREAM_END); 109 110 zerr = inflateEnd(&d_stream); 111 assert(zerr == Z_OK); 112 113 assert(outbufpos == d_stream.total_out); 114 err = unpack_cpio(root, outbuf, outbufpos); 115 free(outbuf); 116 return err; 117} 118 119/* ---------------------- POPULATION FROM BOOTINFO ------------------------- */ 120 121static errval_t write_directory(struct dirent *root, const char *path) 122{ 123 errval_t err; 124 125 assert(path != NULL); 126 assert(root != NULL); 127 128 // walk path, creating/locating directories as we go 129 struct dirent *d = root; 130 while (true) { 131 // remove any leading / 132 while (path[0] == '/') { 133 path++; 134 } 135 136 char *nextsep = strchr(path, '/'); 137 138 // no more /, we have the directory name 139 if (nextsep == NULL) { 140 break; 141 } 142 143 // extract dirname, advance path 144 size_t namelen = nextsep - path; 145 char *dirname = malloc(namelen + 1); 146 assert(dirname != NULL); 147 memcpy(dirname, path, namelen); 148 dirname[namelen] = '\0'; 149 path += namelen; 150 151 // does this directory exist? 152 struct dirent *next; 153 err = ramfs_lookup(d, dirname, &next); 154 if (err_is_ok(err)) { 155 free(dirname); 156 d = next; 157 continue; 158 } else if (err_no(err) != FS_ERR_NOTFOUND) { 159 free(dirname); 160 return err; 161 } 162 163 // create the directory 164 err = ramfs_mkdir(d, dirname, &next); 165 if (err_is_fail(err)) { 166 free(dirname); 167 return err; 168 } 169 d = next; 170 } 171 172 // create the last-level directory 173 char *path_copy = strdup(path); 174 assert(path_copy != NULL); 175 err = ramfs_mkdir(d, path_copy, NULL); 176 if (err_is_fail(err)) { 177 free(path_copy); 178 return err; 179 } 180 181 return SYS_ERR_OK; 182} 183 184static errval_t write_file(struct dirent *root, const char *path, uint8_t *data, 185 size_t len) 186{ 187 errval_t err; 188 189 assert(path != NULL); 190 assert(root != NULL); 191 192 // walk path, creating/locating directories as we go 193 struct dirent *d = root; 194 while (true) { 195 // remove any leading / 196 while (path[0] == '/') { 197 path++; 198 } 199 200 char *nextsep = strchr(path, '/'); 201 202 // no more /, we have the filename 203 if (nextsep == NULL) { 204 break; 205 } 206 207 // extract dirname, advance path 208 size_t namelen = nextsep - path; 209 char *dirname = malloc(namelen + 1); 210 assert(dirname != NULL); 211 memcpy(dirname, path, namelen); 212 dirname[namelen] = '\0'; 213 path += namelen; 214 215 // does this directory exist? 216 struct dirent *next; 217 err = ramfs_lookup(d, dirname, &next); 218 if (err_is_ok(err)) { 219 free(dirname); 220 d = next; 221 continue; 222 } else if (err_no(err) != FS_ERR_NOTFOUND) { 223 free(dirname); 224 return err; 225 } 226 227 // create the directory 228 err = ramfs_mkdir(d, dirname, &next); 229 if (err_is_fail(err)) { 230 free(dirname); 231 return err; 232 } 233 d = next; 234 } 235 236 // create the file! 237 struct dirent *f; 238 char *path_copy = strdup(path); 239 assert(path_copy != NULL); 240 err = ramfs_create(d, path_copy, &f); 241 if (err_is_fail(err)) { 242 free(path_copy); 243 return err; 244 } 245 246 // allocate storage 247 uint8_t *buf; 248 err = ramfs_grow(f, 0, len, &buf); 249 if (err_is_fail(err)) { 250 ramfs_delete(f); 251 return err; 252 } 253 254 // copy the payload 255 memcpy(buf, data, len); 256 257 return SYS_ERR_OK; 258} 259 260static errval_t append_to_file(struct dirent *f, const char *str) 261{ 262 assert(!ramfs_isdir(f)); 263 size_t pos = ramfs_get_size(f); 264 size_t len = strlen(str); 265 errval_t err; 266 267 // allocate storage 268 uint8_t *buf; 269 err = ramfs_grow(f, pos, len + 1, &buf); 270 if (err_is_fail(err)) { 271 return err; 272 } 273 274 // copy the payload 275 memcpy(buf, str, len); 276 277 // terminate with a \n 278 buf[len] = '\n'; 279 280 return SYS_ERR_OK; 281} 282 283// try to remove the 'irrelevant' prefix of a multiboot path 284// eg. "/username/blerg/x86_64/sbin/foo..." becomes "/x86_64/sbin/foo..." 285static const char *remove_prefix(const char *path) 286{ 287 static char *theprefix; 288 static size_t theprefixlen; 289 290 // don't know anything 291 if (theprefix == NULL) { 292 // walk forward until we see '/sbin/' 293 char *sbin = strstr(path, "/sbin/"); 294 if (sbin == NULL || sbin == path) { 295 return path; // give up 296 } 297 298 // walk backward one path element (the architecture) 299 char *prevsep = memrchr(path, '/', sbin - path); 300 if (prevsep == NULL) { 301 return path; // give up 302 } 303 304 // store copy of prefix for future use 305 theprefixlen = prevsep - path; 306 theprefix = malloc(theprefixlen + 1); 307 assert(theprefix != NULL); 308 memcpy(theprefix, path, theprefixlen); 309 theprefix[theprefixlen] = '\0'; 310 311 return prevsep; 312 } else if (strncmp(theprefix, path, theprefixlen) == 0) { 313 // check if stored prefix matches 314 return &path[theprefixlen]; 315 } else { // no match? 316 debug_printf("remove_prefix(): '%s' doesn't match stored prefix '%s'\n", 317 path, theprefix); 318 return path; 319 } 320} 321 322static errval_t getimage(struct mem_region *region, size_t *retlen, 323 lvaddr_t *retdata) 324{ 325 // map in the real module (FIXME: leaking it afterwards) 326 size_t len; 327 errval_t err = spawn_map_module(region, &len, retdata, NULL); 328 if (err_is_ok(err)) { 329 assert(len >= region->mrmod_size); 330 *retlen = region->mrmod_size; 331 } 332 return err; 333} 334 335static void populate_multiboot(struct dirent *root, struct bootinfo *bi) 336{ 337 lvaddr_t data; 338 size_t len; 339 errval_t err; 340 341 assert(root != NULL); 342 343 // create bootscript file 344 struct dirent *bootscript_f; 345 err = ramfs_create(root, BOOTSCRIPT_FILE_NAME, &bootscript_f); 346 if (err_is_fail(err)) { 347 USER_PANIC_ERR(err, "error creating bootscript file"); 348 } 349 350 debug_printf("pre-populating from boot image...\n"); 351 352 for (int i = 0; i < bi->regions_length; i++) { 353 struct mem_region *region = &bi->regions[i]; 354 if (region->mr_type != RegionType_Module) { /* Not of module type */ 355 continue; 356 } 357 358 const char *name = remove_prefix(multiboot_module_name(region)); 359 360 // is this a ramfs image we should unpack? 361 if (strstr(name, "_ramfs.cpio.gz") != NULL) { 362 debug_printf("unpacking Gzipped CPIO %s\n", name); 363 err = spawn_map_module(region, &len, &data, NULL); 364 if (err_is_fail(err)) { 365 USER_PANIC_ERR(err, "error in spawn_map_module"); 366 } 367 368 err = unpack_cpiogz(root, (void *)data, len); 369 if (err_is_fail(err)) { 370 USER_PANIC_ERR(err, "error unpacking ramfs image"); 371 } 372 373 // TODO: unmap 374 } else if (strstr(name, "_ramfs.cpio") != NULL) { 375 debug_printf("unpacking CPIO %s\n", name); 376 err = spawn_map_module(region, &len, &data, NULL); 377 if (err_is_fail(err)) { 378 USER_PANIC_ERR(err, "error in spawn_map_module"); 379 } 380 381 err = unpack_cpio(root, (void *)data, len); 382 if (err_is_fail(err)) { 383 USER_PANIC_ERR(err, "error unpacking ramfs image"); 384 } 385 386 // TODO: unmap 387 } else { 388 // map the image 389 err = getimage(region, &len, &data); 390 if (err_is_fail(err)) { 391 USER_PANIC_ERR(err, "error in getimage"); 392 } 393 394 // copy to ramfs 395 err = write_file(root, name, (void *)data, len); 396 if (err_is_fail(err)) { 397 if (err_no(err) == FS_ERR_EXISTS) { 398 debug_printf("%s already exists, skipping it\n", name); 399 } else { 400 USER_PANIC_ERR(err, "error in write_file"); 401 } 402 } 403 404 // TODO: unmap 405 406 // append line to bootscript 407 const char *args = remove_prefix(multiboot_module_rawstring(region)); 408 char *line = NULL; 409 410 // Prepend a '/' if path is relative 411 if(args[0] != '/') { 412 line = calloc(strlen(args) + 2, 1); 413 line[0] = '/'; 414 strcat(line, args); 415 args = line; 416 } 417 418 err = append_to_file(bootscript_f, args); 419 420 // Free temporary buffer if allocated 421 if(line != NULL) { 422 free(line); 423 } 424 425 if (err_is_fail(err)) { 426 USER_PANIC_ERR(err, "error appending to bootscript"); 427 } 428 } 429 } 430 431 debug_printf("ready\n"); 432} 433 434// Get the bootinfo and map it in. 435static errval_t map_bootinfo(struct bootinfo **bootinfo) 436{ 437 errval_t err, msgerr; 438 439 struct monitor_blocking_binding *cl = get_monitor_blocking_binding(); 440 assert(cl != NULL); 441 442 struct capref bootinfo_frame; 443 size_t bootinfo_size; 444 445 err = slot_alloc(&bootinfo_frame); 446 if (err_is_fail(err)) { 447 return err; 448 } 449 msgerr = cl->rpc_tx_vtbl.get_bootinfo(cl, &err, &bootinfo_frame, &bootinfo_size); 450 if (err_is_fail(msgerr)) { 451 err = msgerr; 452 } 453 if (err_is_fail(err)) { 454 USER_PANIC_ERR(err, "failed in get_bootinfo"); 455 return err; 456 } 457 458 err = vspace_map_one_frame((void**)bootinfo, bootinfo_size, bootinfo_frame, 459 NULL, NULL); 460 assert(err_is_ok(err)); 461 462 return err; 463} 464 465static void multiboot_cap_reply(struct monitor_binding *st, struct capref cap, 466 errval_t msgerr) 467{ 468 errval_t err; 469 static cslot_t multiboot_slots = 0; 470 471 // All multiboot caps received 472 if (err_is_fail(msgerr)) { 473 // Request bootinfo frame 474 struct bootinfo *bi; 475 err = map_bootinfo(&bi); 476 assert(err_is_ok(err)); 477 478 // Init ramfs 479 struct dirent *root = ramfs_init(); 480 481 // Populate it with contents of multiboot 482 populate_multiboot(root, bi); 483 484 // Start the service 485 err = start_service(root); 486 assert(err_is_ok(err)); 487 return; 488 } 489 490 // Move the cap into the multiboot cnode 491 struct capref dest = { 492 .cnode = cnode_module, 493 .slot = multiboot_slots++, 494 }; 495 err = cap_copy(dest, cap); 496 assert(err_is_ok(err)); 497 err = cap_destroy(cap); 498 assert(err_is_ok(err)); 499 500 err = st->tx_vtbl.multiboot_cap_request(st, NOP_CONT, multiboot_slots); 501 assert(err_is_ok(err)); 502} 503 504static void bootstrap(void) 505{ 506 errval_t err; 507 508 /* Create the module cnode */ 509 struct capref modulecn_cap = { 510 .cnode = cnode_root, 511 .slot = ROOTCN_SLOT_MODULECN, 512 }; 513 err = cnode_create_raw(modulecn_cap, NULL, ObjType_L2CNode, 514 ((cslot_t)1 << L2_CNODE_BITS), NULL); 515 if (err_is_fail(err)) { 516 DEBUG_ERR(err, "cnode_create_raw failed"); 517 abort(); 518 } 519 520 // XXX: Set reply handler 521 struct monitor_binding *st = get_monitor_binding(); 522 st->rx_vtbl.multiboot_cap_reply = multiboot_cap_reply; 523 524 // Make first multiboot cap request 525 err = st->tx_vtbl.multiboot_cap_request(st, NOP_CONT, 0); 526 assert(err_is_ok(err)); 527} 528 529int main(int argc, char *argv[]) 530{ 531 // Request multiboot caps and bootinfo from monitor 532 bootstrap(); 533 534 messages_handler_loop(); 535 return 0; 536} 537