xref: /illumos-gate/usr/src/uts/intel/zfs/spa_boot.c (revision 8a88157cd7245729dea5d91a5181bb05a80164a8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/spa.h>
30 #include <sys/sunddi.h>
31 
32 char *
33 spa_get_bootfs()
34 {
35 	char *zfs_bp;
36 
37 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
38 	    DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bp) !=
39 	    DDI_SUCCESS)
40 		return (NULL);
41 	return (zfs_bp);
42 }
43 
44 void
45 spa_free_bootfs(char *bootfs)
46 {
47 	ddi_prop_free(bootfs);
48 }
49 
50 /*
51  * Calculate how many device pathnames are in devpath_list.
52  * The devpath_list could look like this:
53  *
54  *	"/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a"
55  */
56 static int
57 spa_count_devpath(char *devpath_list)
58 {
59 	int numpath;
60 	char *tmp_path, *blank;
61 
62 	numpath = 0;
63 	tmp_path = devpath_list;
64 
65 	/* skip leading blanks */
66 	while (*tmp_path == ' ')
67 		tmp_path++;
68 
69 	while ((blank = strchr(tmp_path, ' ')) != NULL) {
70 
71 		numpath++;
72 		/* skip contiguous blanks */
73 		while (*blank == ' ')
74 			blank++;
75 		tmp_path = blank;
76 	}
77 
78 	if (strlen(tmp_path) > 0)
79 		numpath++;
80 
81 	return (numpath);
82 }
83 
84 /*
85  * Only allow booting the device if it has the same vdev information as
86  * the most recently updated vdev (highest txg) and is in a valid state.
87  *
88  * GRUB passes online/active device path names, e.g.
89  *	"/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a"
90  * to the kernel. The best vdev should have the same matching online/active
91  * list as what GRUB passes in.
92  */
93 static int
94 spa_check_devstate(char *devpath_list, char *dev, nvlist_t *conf)
95 {
96 	nvlist_t *nvtop, **child;
97 	uint_t label_path, grub_path, c, children;
98 	char *type;
99 
100 	VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE,
101 	    &nvtop) == 0);
102 	VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0);
103 
104 	if (strcmp(type, VDEV_TYPE_DISK) == 0)
105 		return (spa_rootdev_validate(nvtop)? 0 : EINVAL);
106 
107 	ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0);
108 
109 	VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN,
110 	    &child, &children) == 0);
111 
112 	/*
113 	 * Check if the devpath_list is the same as the path list in conf.
114 	 * If these two lists are different, then the booting device is not an
115 	 * up-to-date device that can be booted.
116 	 */
117 	label_path = 0;
118 	for (c = 0; c < children; c++) {
119 		char *physpath;
120 
121 		if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH,
122 		    &physpath) != 0)
123 			return (EINVAL);
124 
125 		if (spa_rootdev_validate(child[c])) {
126 			if (strstr(devpath_list, physpath) == NULL)
127 				return (EINVAL);
128 			label_path++;
129 		} else {
130 			char *blank;
131 
132 			if (blank = strchr(dev, ' '))
133 				*blank = '\0';
134 			if (strcmp(physpath, dev) == 0)
135 				return (EINVAL);
136 			if (blank)
137 				*blank = ' ';
138 		}
139 	}
140 
141 	grub_path = spa_count_devpath(devpath_list);
142 
143 	if (label_path != grub_path)
144 		return (EINVAL);
145 
146 	return (0);
147 }
148 
149 /*
150  * Given a list of vdev physpath names, pick the vdev with the most recent txg,
151  * and return the point of the device's physpath in the list and the device's
152  * label configuration. The content of the label would be the most recent
153  * updated information.
154  */
155 int
156 spa_get_rootconf(char *devpath_list, char **bestdev, nvlist_t **bestconf)
157 {
158 	nvlist_t *conf = NULL;
159 	char *dev = NULL;
160 	uint64_t txg = 0;
161 	char *devpath, *blank;
162 
163 	devpath = devpath_list;
164 	dev = devpath;
165 
166 	while (devpath[0] == ' ')
167 		devpath++;
168 
169 	while ((blank = strchr(devpath, ' ')) != NULL) {
170 		*blank = '\0';
171 		spa_check_rootconf(devpath, &dev, &conf, &txg);
172 		*blank = ' ';
173 
174 		while (*blank == ' ')
175 			blank++;
176 		devpath = blank;
177 	}
178 
179 	/* for the only or the last devpath in the devpath_list */
180 	if (strlen(devpath) > 0)
181 		spa_check_rootconf(devpath, &dev, &conf, &txg);
182 
183 	if (conf == NULL)
184 		return (EINVAL);
185 
186 	/*
187 	 * dev/conf is the vdev with the most recent txg.
188 	 * Check if the device is in a bootable state.
189 	 * dev may have a trailing blank since it points to a string
190 	 * in the devpath_list.
191 	 */
192 	if (spa_check_devstate(devpath_list, dev, conf) != 0)
193 		return (EINVAL);
194 
195 	*bestdev = dev;
196 	*bestconf = conf;
197 	return (0);
198 }
199