示例#1
0
static void subdev_close(struct bdev *_dev)
{
	subdev_t *subdev = (subdev_t *)_dev;

	bio_close(subdev->parent);
	subdev->parent = NULL;
}
示例#2
0
文件: fs.c 项目: nroychowdhury/lk
// 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);
}
示例#3
0
文件: fs.c 项目: nroychowdhury/lk
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;

}
示例#4
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;
}
示例#5
0
文件: subdev.c 项目: cpizano/lk
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;
}
示例#6
0
文件: debug.c 项目: 0xBADCA7/lk
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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
文件: commands.c 项目: chychc/lk
// 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";
	}
}
示例#10
0
文件: commands.c 项目: M1cha/lk
// 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";
	}
}