| /* do_mounts_dm.c | 
 |  * Copyright (C) 2010 The Chromium OS Authors <chromium-os-dev@chromium.org> | 
 |  *                    All Rights Reserved. | 
 |  * Based on do_mounts_md.c | 
 |  * | 
 |  * This file is released under the GPL. | 
 |  */ | 
 | #include <linux/async.h> | 
 | #include <linux/ctype.h> | 
 | #include <linux/device-mapper.h> | 
 | #include <linux/fs.h> | 
 | #include <linux/string.h> | 
 | #include <linux/delay.h> | 
 |  | 
 | #include "do_mounts.h" | 
 |  | 
 | #define DM_MAX_DEVICES 256 | 
 | #define DM_MAX_TARGETS 256 | 
 | #define DM_MAX_NAME 32 | 
 | #define DM_MAX_UUID 129 | 
 | #define DM_NO_UUID "none" | 
 |  | 
 | #define DM_MSG_PREFIX "init" | 
 |  | 
 | /* Separators used for parsing the dm= argument. */ | 
 | #define DM_FIELD_SEP " " | 
 | #define DM_LINE_SEP "," | 
 | #define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP | 
 |  | 
 | /* | 
 |  * When the device-mapper and any targets are compiled into the kernel | 
 |  * (not a module), one or more device-mappers may be created and used | 
 |  * as the root device at boot time with the parameters given with the | 
 |  * boot line dm=... | 
 |  * | 
 |  * Multiple device-mappers can be stacked specifing the number of | 
 |  * devices. A device can have multiple targets if the the number of | 
 |  * targets is specified. | 
 |  * | 
 |  * TODO(taysom:defect 32847) | 
 |  * In the future, the <num> field will be mandatory. | 
 |  * | 
 |  * <device>        ::= [<num>] <device-mapper>+ | 
 |  * <device-mapper> ::= <head> "," <target>+ | 
 |  * <head>          ::= <name> <uuid> <mode> [<num>] | 
 |  * <target>        ::= <start> <length> <type> <options> "," | 
 |  * <mode>          ::= "ro" | "rw" | 
 |  * <uuid>          ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" | 
 |  * <type>          ::= "verity" | "bootcache" | ... | 
 |  * | 
 |  * Example: | 
 |  * 2 vboot none ro 1, | 
 |  *     0 1768000 bootcache | 
 |  *       device=aa55b119-2a47-8c45-946a-5ac57765011f+1 | 
 |  *       signature=76e9be054b15884a9fa85973e9cb274c93afadb6 | 
 |  *       cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, | 
 |  *   vroot none ro 1, | 
 |  *     0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 | 
 |  *       root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 | 
 |  *       salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe | 
 |  * | 
 |  * Notes: | 
 |  *  1. uuid is a label for the device and we set it to "none". | 
 |  *  2. The <num> field will be optional initially and assumed to be 1. | 
 |  *     Once all the scripts that set these fields have been set, it will | 
 |  *     be made mandatory. | 
 |  */ | 
 |  | 
 | struct dm_setup_target { | 
 | 	sector_t begin; | 
 | 	sector_t length; | 
 | 	char *type; | 
 | 	char *params; | 
 | 	/* simple singly linked list */ | 
 | 	struct dm_setup_target *next; | 
 | }; | 
 |  | 
 | struct dm_device { | 
 | 	int minor; | 
 | 	int ro; | 
 | 	char name[DM_MAX_NAME]; | 
 | 	char uuid[DM_MAX_UUID]; | 
 | 	unsigned long num_targets; | 
 | 	struct dm_setup_target *target; | 
 | 	int target_count; | 
 | 	struct dm_device *next; | 
 | }; | 
 |  | 
 | struct dm_option { | 
 | 	char *start; | 
 | 	char *next; | 
 | 	size_t len; | 
 | 	char delim; | 
 | }; | 
 |  | 
 | static struct { | 
 | 	unsigned long num_devices; | 
 | 	char *str; | 
 | } dm_setup_args __initdata; | 
 |  | 
 | static __initdata int dm_early_setup; | 
 |  | 
 | static int __init get_dm_option(struct dm_option *opt, const char *accept) | 
 | { | 
 | 	char *str = opt->next; | 
 | 	char *endp; | 
 |  | 
 | 	if (!str) | 
 | 		return 0; | 
 |  | 
 | 	str = skip_spaces(str); | 
 | 	opt->start = str; | 
 | 	endp = strpbrk(str, accept); | 
 | 	if (!endp) {  /* act like strchrnul */ | 
 | 		opt->len = strlen(str); | 
 | 		endp = str + opt->len; | 
 | 	} else { | 
 | 		opt->len = endp - str; | 
 | 	} | 
 | 	opt->delim = *endp; | 
 | 	if (*endp == 0) { | 
 | 		/* Don't advance past the nul. */ | 
 | 		opt->next = endp; | 
 | 	} else { | 
 | 		opt->next = endp + 1; | 
 | 	} | 
 | 	return opt->len != 0; | 
 | } | 
 |  | 
 | static int __init dm_setup_cleanup(struct dm_device *devices) | 
 | { | 
 | 	struct dm_device *dev = devices; | 
 |  | 
 | 	while (dev) { | 
 | 		struct dm_device *old_dev = dev; | 
 | 		struct dm_setup_target *target = dev->target; | 
 | 		while (target) { | 
 | 			struct dm_setup_target *old_target = target; | 
 | 			kfree(target->type); | 
 | 			kfree(target->params); | 
 | 			target = target->next; | 
 | 			kfree(old_target); | 
 | 			dev->target_count--; | 
 | 		} | 
 | 		BUG_ON(dev->target_count); | 
 | 		dev = dev->next; | 
 | 		kfree(old_dev); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static char * __init dm_parse_device(struct dm_device *dev, char *str) | 
 | { | 
 | 	struct dm_option opt; | 
 | 	size_t len; | 
 |  | 
 | 	/* Grab the logical name of the device to be exported to udev */ | 
 | 	opt.next = str; | 
 | 	if (!get_dm_option(&opt, DM_FIELD_SEP)) { | 
 | 		DMERR("failed to parse device name"); | 
 | 		goto parse_fail; | 
 | 	} | 
 | 	len = min(opt.len + 1, sizeof(dev->name)); | 
 | 	strlcpy(dev->name, opt.start, len);  /* includes nul */ | 
 |  | 
 | 	/* Grab the UUID value or "none" */ | 
 | 	if (!get_dm_option(&opt, DM_FIELD_SEP)) { | 
 | 		DMERR("failed to parse device uuid"); | 
 | 		goto parse_fail; | 
 | 	} | 
 | 	len = min(opt.len + 1, sizeof(dev->uuid)); | 
 | 	strlcpy(dev->uuid, opt.start, len); | 
 |  | 
 | 	/* Determine if the table/device will be read only or read-write */ | 
 | 	get_dm_option(&opt, DM_ANY_SEP); | 
 | 	if (!strncmp("ro", opt.start, opt.len)) { | 
 | 		dev->ro = 1; | 
 | 	} else if (!strncmp("rw", opt.start, opt.len)) { | 
 | 		dev->ro = 0; | 
 | 	} else { | 
 | 		DMERR("failed to parse table mode"); | 
 | 		goto parse_fail; | 
 | 	} | 
 |  | 
 | 	/* Optional number field */ | 
 | 	/* XXX: The <num> field will be mandatory in the next round */ | 
 | 	if (opt.delim == DM_FIELD_SEP[0]) { | 
 | 		if (!get_dm_option(&opt, DM_LINE_SEP)) | 
 | 			return NULL; | 
 | 		dev->num_targets = simple_strtoul(opt.start, NULL, 10); | 
 | 	} else { | 
 | 		dev->num_targets = 1; | 
 | 	} | 
 | 	if (dev->num_targets > DM_MAX_TARGETS) { | 
 | 		DMERR("too many targets %lu > %d", | 
 | 			dev->num_targets, DM_MAX_TARGETS); | 
 | 	} | 
 | 	return opt.next; | 
 |  | 
 | parse_fail: | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static char * __init dm_parse_targets(struct dm_device *dev, char *str) | 
 | { | 
 | 	struct dm_option opt; | 
 | 	struct dm_setup_target **target = &dev->target; | 
 | 	unsigned long num_targets = dev->num_targets; | 
 | 	unsigned long i; | 
 |  | 
 | 	/* Targets are defined as per the table format but with a | 
 | 	 * comma as a newline separator. */ | 
 | 	opt.next = str; | 
 | 	for (i = 0; i < num_targets; i++) { | 
 | 		*target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); | 
 | 		if (!*target) { | 
 | 			DMERR("failed to allocate memory for target %s<%ld>", | 
 | 				dev->name, i); | 
 | 			goto parse_fail; | 
 | 		} | 
 | 		dev->target_count++; | 
 |  | 
 | 		if (!get_dm_option(&opt, DM_FIELD_SEP)) { | 
 | 			DMERR("failed to parse starting sector" | 
 | 				" for target %s<%ld>", dev->name, i); | 
 | 			goto parse_fail; | 
 | 		} | 
 | 		(*target)->begin = simple_strtoull(opt.start, NULL, 10); | 
 |  | 
 | 		if (!get_dm_option(&opt, DM_FIELD_SEP)) { | 
 | 			DMERR("failed to parse length for target %s<%ld>", | 
 | 				dev->name, i); | 
 | 			goto parse_fail; | 
 | 		} | 
 | 		(*target)->length = simple_strtoull(opt.start, NULL, 10); | 
 |  | 
 | 		if (get_dm_option(&opt, DM_FIELD_SEP)) | 
 | 			(*target)->type = kstrndup(opt.start, opt.len, | 
 | 							GFP_KERNEL); | 
 | 		if (!((*target)->type)) { | 
 | 			DMERR("failed to parse type for target %s<%ld>", | 
 | 				dev->name, i); | 
 | 			goto parse_fail; | 
 | 		} | 
 | 		if (get_dm_option(&opt, DM_LINE_SEP)) | 
 | 			(*target)->params = kstrndup(opt.start, opt.len, | 
 | 							GFP_KERNEL); | 
 | 		if (!((*target)->params)) { | 
 | 			DMERR("failed to parse params for target %s<%ld>", | 
 | 				dev->name, i); | 
 | 			goto parse_fail; | 
 | 		} | 
 | 		target = &((*target)->next); | 
 | 	} | 
 | 	DMDEBUG("parsed %d targets", dev->target_count); | 
 |  | 
 | 	return opt.next; | 
 |  | 
 | parse_fail: | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static struct dm_device * __init dm_parse_args(void) | 
 | { | 
 | 	struct dm_device *devices = NULL; | 
 | 	struct dm_device **tail = &devices; | 
 | 	struct dm_device *dev; | 
 | 	char *str = dm_setup_args.str; | 
 | 	unsigned long num_devices = dm_setup_args.num_devices; | 
 | 	unsigned long i; | 
 |  | 
 | 	if (!str) | 
 | 		return NULL; | 
 | 	for (i = 0; i < num_devices; i++) { | 
 | 		dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 
 | 		if (!dev) { | 
 | 			DMERR("failed to allocated memory for dev"); | 
 | 			goto error; | 
 | 		} | 
 | 		*tail = dev; | 
 | 		tail = &dev->next; | 
 | 		/* | 
 | 		 * devices are given minor numbers 0 - n-1 | 
 | 		 * in the order they are found in the arg | 
 | 		 * string. | 
 | 		 */ | 
 | 		dev->minor = i; | 
 | 		str = dm_parse_device(dev, str); | 
 | 		if (!str)	/* NULL indicates error in parsing, bail */ | 
 | 			goto error; | 
 |  | 
 | 		str = dm_parse_targets(dev, str); | 
 | 		if (!str) | 
 | 			goto error; | 
 | 	} | 
 | 	return devices; | 
 | error: | 
 | 	dm_setup_cleanup(devices); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * Parse the command-line parameters given our kernel, but do not | 
 |  * actually try to invoke the DM device now; that is handled by | 
 |  * dm_setup_drives after the low-level disk drivers have initialised. | 
 |  * dm format is described at the top of the file. | 
 |  * | 
 |  * Because dm minor numbers are assigned in assending order starting with 0, | 
 |  * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1, | 
 |  * and so forth. | 
 |  */ | 
 | static int __init dm_setup(char *str) | 
 | { | 
 | 	struct dm_option opt; | 
 | 	unsigned long num_devices; | 
 |  | 
 | 	if (!str) { | 
 | 		DMDEBUG("str is NULL"); | 
 | 		goto parse_fail; | 
 | 	} | 
 | 	opt.next = str; | 
 | 	if (!get_dm_option(&opt, DM_FIELD_SEP)) | 
 | 		goto parse_fail; | 
 | 	if (isdigit(opt.start[0])) {	/* XXX: Optional number field */ | 
 | 		num_devices = simple_strtoul(opt.start, NULL, 10); | 
 | 		str = opt.next; | 
 | 	} else { | 
 | 		num_devices = 1; | 
 | 		/* Don't advance str */ | 
 | 	} | 
 | 	if (num_devices > DM_MAX_DEVICES) { | 
 | 		DMDEBUG("too many devices %lu > %d", | 
 | 			num_devices, DM_MAX_DEVICES); | 
 | 	} | 
 | 	dm_setup_args.str = str; | 
 | 	dm_setup_args.num_devices = num_devices; | 
 | 	DMINFO("will configure %lu devices", num_devices); | 
 | 	dm_early_setup = 1; | 
 | 	return 1; | 
 |  | 
 | parse_fail: | 
 | 	DMWARN("Invalid arguments supplied to dm=."); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Number of milliseconds to wait before checking for drive status */ | 
 | #define DM_DRIVE_WAIT			20 | 
 | /* Number of tries allowed to wait for DM drive, before timeout */ | 
 | #define DM_DRIVE_RETRY_COUNT		100 | 
 |  | 
 | static int __init dm_wait_for_drive(char *params) | 
 | { | 
 | 	char *dm_params; | 
 | 	int ret, argc = 0, try = 0; | 
 | 	char **argv; | 
 | 	dev_t dm_dev; | 
 |  | 
 | 	dm_params = kstrndup(params, strlen(params), GFP_KERNEL); | 
 | 	if (!dm_params) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = dm_split_args(&argc, &argv, dm_params); | 
 | 	if (ret || argc < 2) { | 
 | 		DMDEBUG("failed to get dm params"); | 
 | 		goto free_dm_params; | 
 | 	} | 
 |  | 
 | 	dm_dev = dm_get_dev_t(argv[1]); | 
 | 	while (!dm_dev && try++ < DM_DRIVE_RETRY_COUNT) { | 
 | 		DMDEBUG("Waiting for device %s\n", argv[1]); | 
 | 		msleep(DM_DRIVE_WAIT); | 
 | 		dm_dev = dm_get_dev_t(argv[1]); | 
 | 	} | 
 |  | 
 | 	if (!dm_dev) { | 
 | 		ret = -ENODEV; | 
 | 		goto free_dm_params; | 
 | 	} | 
 |  | 
 | 	DMDEBUG("Device %s found\n", argv[1]); | 
 |  | 
 | free_dm_params: | 
 | 	kfree(dm_params); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void __init dm_setup_drives(void) | 
 | { | 
 | 	struct mapped_device *md = NULL; | 
 | 	struct dm_table *table = NULL; | 
 | 	struct dm_setup_target *target; | 
 | 	struct dm_device *dev; | 
 | 	char *uuid; | 
 | 	fmode_t fmode = FMODE_READ; | 
 | 	struct dm_device *devices; | 
 |  | 
 | 	devices = dm_parse_args(); | 
 |  | 
 | 	for (dev = devices; dev; dev = dev->next) { | 
 | 		if (dm_create(dev->minor, &md)) { | 
 | 			DMDEBUG("failed to create the device"); | 
 | 			goto dm_create_fail; | 
 | 		} | 
 | 		DMDEBUG("created device '%s'", dm_device_name(md)); | 
 |  | 
 | 		/* | 
 | 		 * In addition to flagging the table below, the disk must be | 
 | 		 * set explicitly ro/rw. | 
 | 		 */ | 
 | 		set_disk_ro(dm_disk(md), dev->ro); | 
 |  | 
 | 		if (!dev->ro) | 
 | 			fmode |= FMODE_WRITE; | 
 | 		if (dm_table_create(&table, fmode, dev->target_count, md)) { | 
 | 			DMDEBUG("failed to create the table"); | 
 | 			goto dm_table_create_fail; | 
 | 		} | 
 |  | 
 | 		dm_lock_md_type(md); | 
 |  | 
 | 		for (target = dev->target; target; target = target->next) { | 
 | 			DMINFO("adding target '%llu %llu %s %s'", | 
 | 			       (unsigned long long) target->begin, | 
 | 			       (unsigned long long) target->length, | 
 | 			       target->type, target->params); | 
 |  | 
 | 			if (dm_wait_for_drive(target->params)) | 
 | 				goto add_target_fail; | 
 |  | 
 | 			if (dm_table_add_target(table, target->type, | 
 | 						target->begin, | 
 | 						target->length, | 
 | 						target->params)) { | 
 | 				DMDEBUG("failed to add the target" | 
 | 					" to the table"); | 
 | 				goto add_target_fail; | 
 | 			} | 
 | 		} | 
 | 		if (dm_table_complete(table)) { | 
 | 			DMDEBUG("failed to complete the table"); | 
 | 			goto table_complete_fail; | 
 | 		} | 
 |  | 
 | 		/* Suspend the device so that we can bind it to the table. */ | 
 | 		if (dm_suspend(md, 0)) { | 
 | 			DMDEBUG("failed to suspend the device pre-bind"); | 
 | 			goto suspend_fail; | 
 | 		} | 
 |  | 
 | 		/* Initial table load: acquire type of table. */ | 
 | 		dm_set_md_type(md, dm_table_get_type(table)); | 
 |  | 
 | 		/* Setup md->queue to reflect md's type. */ | 
 | 		if (dm_setup_md_queue(md, table)) { | 
 | 			DMWARN("unable to set up device queue for new table."); | 
 | 			goto setup_md_queue_fail; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Bind the table to the device. This is the only way | 
 | 		 * to associate md->map with the table and set the disk | 
 | 		 * capacity directly. | 
 | 		 */ | 
 | 		if (dm_swap_table(md, table)) {  /* should return NULL. */ | 
 | 			DMDEBUG("failed to bind the device to the table"); | 
 | 			goto table_bind_fail; | 
 | 		} | 
 |  | 
 | 		/* Finally, resume and the device should be ready. */ | 
 | 		if (dm_resume(md)) { | 
 | 			DMDEBUG("failed to resume the device"); | 
 | 			goto resume_fail; | 
 | 		} | 
 |  | 
 | 		/* Export the dm device via the ioctl interface */ | 
 | 		if (!strcmp(DM_NO_UUID, dev->uuid)) | 
 | 			uuid = NULL; | 
 | 		if (dm_ioctl_export(md, dev->name, uuid)) { | 
 | 			DMDEBUG("failed to export device with given" | 
 | 				" name and uuid"); | 
 | 			goto export_fail; | 
 | 		} | 
 |  | 
 | 		dm_unlock_md_type(md); | 
 |  | 
 | 		DMINFO("dm-%d is ready", dev->minor); | 
 | 	} | 
 | 	dm_setup_cleanup(devices); | 
 | 	return; | 
 |  | 
 | export_fail: | 
 | resume_fail: | 
 | table_bind_fail: | 
 | setup_md_queue_fail: | 
 | suspend_fail: | 
 | table_complete_fail: | 
 | add_target_fail: | 
 | 	dm_unlock_md_type(md); | 
 | dm_table_create_fail: | 
 | 	dm_put(md); | 
 | dm_create_fail: | 
 | 	DMWARN("starting dm-%d (%s) failed", | 
 | 	       dev->minor, dev->name); | 
 | 	dm_setup_cleanup(devices); | 
 | } | 
 |  | 
 | __setup("dm=", dm_setup); | 
 |  | 
 | void __init dm_run_setup(void) | 
 | { | 
 | 	if (!dm_early_setup) | 
 | 		return; | 
 | 	DMINFO("attempting early device configuration."); | 
 | 	dm_setup_drives(); | 
 | } |