static void subdev_close(struct bdev *_dev) { subdev_t *subdev = (subdev_t *)_dev; bio_close(subdev->parent); subdev->parent = NULL; }
// decrement the ref to the mount structure, which may // cause an unmount operation static void put_mount(struct fs_mount *mount) { mutex_acquire(&mount_lock); if ((--mount->ref) == 0) { list_delete(&mount->node); mount->api->unmount(mount->cookie); free(mount->path); if (mount->dev) bio_close(mount->dev); free(mount); } mutex_release(&mount_lock); }
static status_t mount(const char *path, const char *device, const struct fs_api *api) { struct fs_mount *mount; char temppath[FS_MAX_PATH_LEN]; strlcpy(temppath, path, sizeof(temppath)); fs_normalize_path(temppath); if (temppath[0] != '/') return ERR_BAD_PATH; /* see if there's already something at this path, abort if there is */ mount = find_mount(temppath, NULL); if (mount) { put_mount(mount); return ERR_ALREADY_MOUNTED; } /* open a bio device if the string is nonnull */ bdev_t *dev = NULL; if (device && device[0] != '\0') { dev = bio_open(device); if (!dev) return ERR_NOT_FOUND; } /* call into the fs implementation */ fscookie *cookie; status_t err = api->mount(dev, &cookie); if (err < 0) { if (dev) bio_close(dev); return err; } /* create the mount structure and add it to the list */ mount = malloc(sizeof(struct fs_mount)); mount->path = strdup(temppath); mount->dev = dev; mount->cookie = cookie; mount->ref = 1; mount->api = api; list_add_head(&mounts, &mount->node); return 0; }
static int cmd_bio(int argc, const cmd_args *argv) { int rc = 0; if (argc < 2) { printf("not enough arguments:\n"); usage: printf("%s list\n", argv[0].str); printf("%s read <device> <address> <offset> <len>\n", argv[0].str); printf("%s write <device> <address> <offset> <len>\n", argv[0].str); printf("%s erase <device> <offset> <len>\n", argv[0].str); printf("%s ioctl <device> <request> <arg>\n", argv[0].str); printf("%s remove <device>\n", argv[0].str); #if WITH_LIB_PARTITION printf("%s partscan <device> [offset]\n", argv[0].str); #endif return -1; } if (!strcmp(argv[1].str, "list")) { bio_dump_devices(); } else if (!strcmp(argv[1].str, "read")) { if (argc < 6) { printf("not enough arguments:\n"); goto usage; } addr_t address = argv[3].u; off_t offset = argv[4].u; // XXX use long size_t len = argv[5].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } time_t t = current_time(); ssize_t err = bio_read(dev, (void *)address, offset, len); t = current_time() - t; dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "write")) { if (argc < 6) { printf("not enough arguments:\n"); goto usage; } addr_t address = argv[3].u; off_t offset = argv[4].u; // XXX use long size_t len = argv[5].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } time_t t = current_time(); ssize_t err = bio_write(dev, (void *)address, offset, len); t = current_time() - t; dprintf(INFO, "bio_write returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "erase")) { if (argc < 5) { printf("not enough arguments:\n"); goto usage; } off_t offset = argv[3].u; // XXX use long size_t len = argv[4].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } time_t t = current_time(); ssize_t err = bio_erase(dev, offset, len); t = current_time() - t; dprintf(INFO, "bio_erase returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "ioctl")) { if (argc < 4) { printf("not enough arguments:\n"); goto usage; } int request = argv[3].u; int arg = (argc == 5) ? argv[4].u : 0; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } int err = bio_ioctl(dev, request, (void *)arg); dprintf(INFO, "bio_ioctl returns %d\n", err); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "remove")) { if (argc < 3) { printf("not enough arguments:\n"); goto usage; } bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } bio_unregister_device(dev); bio_close(dev); #if WITH_LIB_PARTITION } else if (!strcmp(argv[1].str, "partscan")) { if (argc < 3) { printf("not enough arguments:\n"); goto usage; } off_t offset = 0; if (argc > 3) offset = argv[3].u; rc = partition_publish(argv[2].str, offset); dprintf(INFO, "partition_publish returns %d\n", rc); #endif } else { printf("unrecognized subcommand\n"); goto usage; } return rc; }
status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, bnum_t block_count) { status_t err = NO_ERROR; bdev_t *parent = NULL; subdev_t *sub = NULL; size_t geometry_count; bio_erase_geometry_info_t* geometry; LTRACEF("parent \"%s\", sub \"%s\", startblock %u, count %u\n", parent_dev, subdev, startblock, block_count); // Make sure our parent exists parent = bio_open(parent_dev); if (!parent) { LTRACEF("Failed to find parent \"%s\"\n", parent_dev); BAIL(ERR_NOT_FOUND); } // Allocate our sub-device. sub = malloc(sizeof(subdev_t)); if (!sub) { LTRACEF("Failed to allocate subdevice\n"); BAIL(ERR_NO_MEMORY); } /* Make sure we're able to do this. If the device has a specified erase * geometry, the specified sub-region must exist entirely with one of our * parent's erase regions, and be aligned to an erase unit boundary. * Otherwise, the specified region must simply exist within the block range * of the device. */ if (parent->geometry_count && parent->geometry) { uint64_t byte_start = ((uint64_t)startblock << parent->block_shift); uint64_t byte_size = ((uint64_t)block_count << parent->block_shift); const bio_erase_geometry_info_t* geo = NULL; LTRACEF("Searching geometry for region which contains @[0x%llx, 0x%llx)\n", byte_start, byte_start + byte_size); // Start by finding the erase region which completely contains the requested range. for (size_t i = 0; i < parent->geometry_count; ++i) { geo = parent->geometry + i; LTRACEF("Checking geometry @[0x%llx, 0x%llx) erase size 0x%zx\n", geo->start, geo->start + geo->size, geo->erase_size); if (bio_contains_range(geo->start, geo->size, byte_start, byte_size)) break; } if (!geo) { LTRACEF("No suitable erase region found\n"); BAIL(ERR_INVALID_ARGS); } // Now check out alignment. uint64_t erase_mask = ((uint64_t)0x1 << geo->erase_shift) - 1; if ((byte_start & erase_mask) || (byte_size & erase_mask)) { LTRACEF("Requested region has improper alignment/length\n"); BAIL(ERR_INVALID_ARGS); } geometry_count = 1; geometry = &sub->geometry; geometry->start = 0; geometry->size = byte_size; geometry->erase_size = geo->erase_size; geometry->erase_shift = geo->erase_shift; } else { bnum_t endblock = startblock + block_count; if ((endblock < startblock) || (endblock > parent->block_count)) BAIL(ERR_INVALID_ARGS); geometry_count = 0; geometry = NULL; } bio_initialize_bdev(&sub->dev, subdev, parent->block_size, block_count, geometry_count, geometry); sub->parent = parent; sub->offset = startblock; sub->dev.read = &subdev_read; sub->dev.read_block = &subdev_read_block; sub->dev.write = &subdev_write; sub->dev.write_block = &subdev_write_block; sub->dev.erase = &subdev_erase; sub->dev.close = &subdev_close; bio_register_device(&sub->dev); bailout: if (err < 0) { if (NULL != parent) bio_close(parent); free(sub); } return err; }
static int cmd_bio(int argc, const cmd_args *argv) { int rc = 0; if (argc < 2) { notenoughargs: printf("not enough arguments:\n"); usage: printf("%s list\n", argv[0].str); printf("%s read <device> <address> <offset> <len>\n", argv[0].str); printf("%s write <device> <address> <offset> <len>\n", argv[0].str); printf("%s dump <device> <offset> <len>\n", argv[0].str); printf("%s erase <device> <offset> <len>\n", argv[0].str); printf("%s ioctl <device> <request> <arg>\n", argv[0].str); printf("%s remove <device>\n", argv[0].str); printf("%s test <device>\n", argv[0].str); #if WITH_LIB_PARTITION printf("%s partscan <device> [offset]\n", argv[0].str); #endif #if WITH_LIB_CKSUM printf("%s crc32 <device> <offset> <len> [repeat]\n", argv[0].str); #endif return -1; } if (!strcmp(argv[1].str, "list")) { bio_dump_devices(); } else if (!strcmp(argv[1].str, "read")) { if (argc < 6) goto notenoughargs; addr_t address = argv[3].u; off_t offset = argv[4].u; // XXX use long size_t len = argv[5].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } lk_time_t t = current_time(); ssize_t err = bio_read(dev, (void *)address, offset, len); t = current_time() - t; dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "write")) { if (argc < 6) goto notenoughargs; addr_t address = argv[3].u; off_t offset = argv[4].u; // XXX use long size_t len = argv[5].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } lk_time_t t = current_time(); ssize_t err = bio_write(dev, (void *)address, offset, len); t = current_time() - t; dprintf(INFO, "bio_write returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "dump")) { if (argc < 5) { printf("not enough arguments:\n"); goto usage; } off_t offset = argv[3].u; // XXX use long size_t len = argv[4].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } uint8_t *buf = memalign(CACHE_LINE, 256); ssize_t err = 0; while (len > 0) { size_t amt = MIN(256, len); ssize_t err = bio_read(dev, buf, offset, amt); if (err < 0) { dprintf(ALWAYS, "read error %s %zu@%zu (err %d)\n", argv[2].str, amt, (size_t)offset, (int)err); break; } DEBUG_ASSERT((size_t)err <= amt); hexdump8_ex(buf, err, offset); if ((size_t)err != amt) { dprintf(ALWAYS, "short read from %s @%zu (wanted %zu, got %zu)\n", argv[2].str, (size_t)offset, amt, (size_t)err); break; } offset += amt; len -= amt; } bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "erase")) { if (argc < 5) goto notenoughargs; off_t offset = argv[3].u; // XXX use long size_t len = argv[4].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } lk_time_t t = current_time(); ssize_t err = bio_erase(dev, offset, len); t = current_time() - t; dprintf(INFO, "bio_erase returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "ioctl")) { if (argc < 4) goto notenoughargs; int request = argv[3].u; int arg = (argc == 5) ? argv[4].u : 0; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } int err = bio_ioctl(dev, request, (void *)arg); dprintf(INFO, "bio_ioctl returns %d\n", err); bio_close(dev); rc = err; } else if (!strcmp(argv[1].str, "remove")) { if (argc < 3) goto notenoughargs; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } bio_unregister_device(dev); bio_close(dev); } else if (!strcmp(argv[1].str, "test")) { if (argc < 3) goto notenoughargs; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } int err = bio_test_device(dev); bio_close(dev); rc = err; #if WITH_LIB_PARTITION } else if (!strcmp(argv[1].str, "partscan")) { if (argc < 3) goto notenoughargs; off_t offset = 0; if (argc > 3) offset = argv[3].u; rc = partition_publish(argv[2].str, offset); dprintf(INFO, "partition_publish returns %d\n", rc); #endif #if WITH_LIB_CKSUM } else if (!strcmp(argv[1].str, "crc32")) { if (argc < 5) goto notenoughargs; off_t offset = argv[3].u; // XXX use long size_t len = argv[4].u; bdev_t *dev = bio_open(argv[2].str); if (!dev) { printf("error opening block device\n"); return -1; } void *buf = malloc(dev->block_size); bool repeat = false; if (argc >= 6 && !strcmp(argv[5].str, "repeat")) { repeat = true; } do { ulong crc = 0; off_t pos = offset; while (pos < offset + len) { ssize_t err = bio_read(dev, buf, pos, MIN(len - (pos - offset), dev->block_size)); if (err <= 0) { printf("error reading at offset 0x%llx\n", offset + pos); break; } crc = crc32(crc, buf, err); pos += err; } printf("crc 0x%08lx\n", crc); } while (repeat); bio_close(dev); free(buf); #endif } else { printf("unrecognized subcommand\n"); goto usage; } return rc; }
int eel_call(int handle, int pos) { int res = 1; int depth = 0; eel_push_context(); eel_current.script = handle; eel_current.bio = bio_open(eel_scripttab[handle].data, eel_scripttab[handle].len); if(!eel_current.bio) { eel_error("INTERNAL ERROR: Couldn't read script!"); eel_pop_context(); return -1; } bio_seek(eel_current.bio, pos, SEEK_SET); eel_current.lval = NULL; while(res > 0) { /* Start of statement */ eel_clear_args(0); eel_current.arg = 0; switch(eel_lex(0)) { case 0: /* End of script */ res = 0; break; case ';': /* Empty statement */ res = 1; break; case '{': /* Begin scope */ eel_push_scope(); ++depth; res = 1; break; case '}': /* End scope */ if(depth) { eel_pop_scope(); --depth; } else res = 0; break; case TK_NEWSYM: res = assignment(TK_NEWSYM); break; case TK_SYMREF: switch(eel_current.lval->value.sym->type) { case EST_CONSTANT: /* Just to get a more * sensible error message... */ case EST_UNDEFINED: case EST_VARIABLE: res = assignment(TK_SYMREF); break; /* case EST_COMMAND: res = command(); break;*/ case EST_FUNCTION: res = function(); break; case EST_DIRECTIVE: case EST_SPECIAL: res = directive(); break; case EST_ENUM: case EST_LABEL: eel_error("Syntax error!"); res = -1; break; case EST_OPERATOR: res = command(); break; default: eel_error("INTERNAL ERROR: Lexer returned" " symbol of illegal type!"); res = -1; break; } break; default: eel_error("Unexpected character!"); res = -1; break; } } eel_current.arg = 0; bio_close(eel_current.bio); eel_pop_context(); return res; }
static int function(void) { int arg, res; eel_symbol_t *func = eel_current.lval->value.sym; eel_push_scope(); /* Grab arguments */ res = eel_parse_args(",", ';'); /* Enter function */ eel_push_context(); if(eel_current.script == func->token) eel_current.bio = bio_clone(eel_current.bio); else eel_current.bio = bio_open(eel_scripttab[func->token].data, eel_scripttab[func->token].len); if(!eel_current.bio) { eel_error("INTERNAL ERROR: Failed to set up script" " reading at start of function!"); eel_pop_context(); eel_pop_scope(); return -1; } res = bio_seek(eel_current.bio, func->data.value.a, SEEK_SET); /* * Find out where to stuff the arguments. */ for(arg = 0; (arg < eel_arg_count) && (res >= 0); ) switch(eel_lex(0)) { case ',': /* Already verified, so we just skip them. */ break; case TK_NEWSYM: { eel_symbol_t *sym; sym = eel_set_data(eel_current.lval->value.s, eel_args + arg); if(sym) { sym->type = EST_VARIABLE; ++arg; } else { eel_error("INTERNAL ERROR: Failed to " "create argument variable!"); res = -1; } break; } case TK_SYMREF: eel_error("Function argument clashes with " "previously defined symbol!"); res = -1; break; default: eel_error("Syntax error in argument list!"); res = -1; break; } /* Find start of body ("the code") */ if(res >= 0) { if(eel_lex(0) != ')') { eel_error("Expected ')'!"); res = -1; } else if(eel_lex(0) != '{') { eel_error("Expected '{'!"); res = -1; } } if(res >= 0) { /* * Execute function * * Note that this is a simple, inefficient * RECURSIVE implementation! * * Alternatively, we could set up a "trigger" * to run "the rest of this function" when * the function ends - and then just return. */ res = eel_call(eel_current.script, bio_tell(eel_current.bio)); } /* Leave function */ bio_close(eel_current.bio); eel_pop_context(); eel_pop_scope(); if(res >= 0) return 1; else return res; }
// return NULL for success, error string for failure const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len) { struct lkb_command *lcmd; for (lcmd = lkb_cmd_list; lcmd; lcmd = lcmd->next) { if (!strcmp(lcmd->name, cmd)) { return lcmd->handler(lkb, arg, len, lcmd->cookie); } } if (len > lkb_iobuffer_size) { return "buffer too small"; } if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) { struct ptable_entry entry; bdev_t *bdev; if (ptable_find(arg, &entry) < 0) { return "no such partition"; } if (len > entry.length) { return "partition too small"; } if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } if (!(bdev = bio_open(bootdevice))) { return "bio_open failed"; } if (bio_erase(bdev, entry.offset, entry.length) != (ssize_t)entry.length) { bio_close(bdev); return "bio_erase failed"; } if (!strcmp(cmd, "flash")) { if (bio_write(bdev, lkb_iobuffer, entry.offset, len) != (ssize_t)len) { bio_close(bdev); return "bio_write failed"; } } bio_close(bdev); return NULL; } else if (!strcmp(cmd, "fpga")) { #if PLATFORM_ZYNQ unsigned *x = lkb_iobuffer; if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } for (unsigned n = 0; n < len; n+= 4) { *x = SWAP_32(*x); x++; } zynq_reset_fpga(); zynq_program_fpga(lkb_iobuffer_phys, len); return NULL; #else return "no fpga"; #endif } else if (!strcmp(cmd, "boot")) { if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } thread_resume(thread_create("ramboot", &do_ramboot, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); return NULL; } else if (!strcmp(cmd, "getsysparam")) { const void *ptr; size_t len; if (sysparam_get_ptr(arg, &ptr, &len) == 0) { lkb_write(lkb, ptr, len); } return NULL; } else if (!strcmp(cmd, "reboot")) { thread_resume(thread_create("reboot", &do_reboot, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); return NULL; } else { return "unknown command"; } }
// return NULL for success, error string for failure const char *lkb_handle_command(lkb_t *lkb, const char *cmd, const char *arg, unsigned len) { struct lkb_command *lcmd; for (lcmd = lkb_cmd_list; lcmd; lcmd = lcmd->next) { if (!strcmp(lcmd->name, cmd)) { return lcmd->handler(lkb, arg, len, lcmd->cookie); } } if (len > lkb_iobuffer_size) { return "buffer too small"; } if (!strcmp(cmd, "flash") || !strcmp(cmd, "erase")) { struct ptable_entry entry; bdev_t *bdev; if (ptable_find(arg, &entry) < 0) { size_t plen = len; /* doesn't exist, make one */ #if PLATFORM_ZYNQ /* XXX not really the right place, should be in the ptable/bio layer */ plen = ROUNDUP(plen, 256*1024); #endif off_t off = ptable_allocate(plen, 0); if (off < 0) { return "no space to allocate partition"; } if (ptable_add(arg, off, plen, 0) < 0) { return "error creating partition"; } if (ptable_find(arg, &entry) < 0) { return "couldn't find partition after creating it"; } } if (len > entry.length) { return "partition too small"; } if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } if (!(bdev = bio_open(bootdevice))) { return "bio_open failed"; } if (bio_erase(bdev, entry.offset, entry.length) != (ssize_t)entry.length) { bio_close(bdev); return "bio_erase failed"; } if (!strcmp(cmd, "flash")) { if (bio_write(bdev, lkb_iobuffer, entry.offset, len) != (ssize_t)len) { bio_close(bdev); return "bio_write failed"; } } bio_close(bdev); return NULL; } else if (!strcmp(cmd, "remove")) { if (ptable_remove(arg) < 0) { return "remove failed"; } return NULL; } else if (!strcmp(cmd, "fpga")) { #if PLATFORM_ZYNQ unsigned *x = lkb_iobuffer; if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } for (unsigned n = 0; n < len; n+= 4) { *x = SWAP_32(*x); x++; } zynq_reset_fpga(); zynq_program_fpga(lkb_iobuffer_phys, len); return NULL; #else return "no fpga"; #endif } else if (!strcmp(cmd, "boot")) { if (lkb_read(lkb, lkb_iobuffer, len)) { return "io error"; } thread_resume(thread_create("boot", &do_boot, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); return NULL; } else if (!strcmp(cmd, "getsysparam")) { const void *ptr; size_t len; if (sysparam_get_ptr(arg, &ptr, &len) == 0) { lkb_write(lkb, ptr, len); } return NULL; } else if (!strcmp(cmd, "reboot")) { thread_resume(thread_create("reboot", &do_reboot, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); return NULL; } else { return "unknown command"; } }