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