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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This file contains all the functions that implement the following 28 * GRUB commands: 29 * kernel, kernel$, module, module$, findroot, bootfs 30 * Return 0 on success, errno on failure. 31 */ 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <assert.h> 35 #include <alloca.h> 36 #include <errno.h> 37 #include <strings.h> 38 #include <unistd.h> 39 #include <fcntl.h> 40 #include <sys/types.h> 41 #include <sys/fs/ufs_mount.h> 42 #include <sys/dktp/fdisk.h> 43 #if defined(__i386) 44 #include <sys/x86_archext.h> 45 #endif /* __i386 */ 46 47 #include "libgrub_impl.h" 48 49 #define RESET_MODULE(barg) ((barg)->gb_module[0] = 0) 50 51 #if defined(__i386) 52 static const char cpuid_dev[] = "/dev/cpu/self/cpuid"; 53 54 /* 55 * Return 1 if the system supports 64-bit mode, 0 if it doesn't, 56 * or -1 on failure. 57 */ 58 static int 59 cpuid_64bit_capable(void) 60 { 61 int fd, ret = -1; 62 struct { 63 uint32_t cp_eax, cp_ebx, cp_ecx, cp_edx; 64 } cpuid_regs; 65 66 if ((fd = open(cpuid_dev, O_RDONLY)) == -1) 67 return (ret); 68 69 if (pread(fd, &cpuid_regs, sizeof (cpuid_regs), 0x80000001) == 70 sizeof (cpuid_regs)) 71 ret = ((CPUID_AMD_EDX_LM & cpuid_regs.cp_edx) != 0); 72 73 (void) close(fd); 74 return (ret); 75 } 76 #endif /* __i386 */ 77 78 79 /* 80 * Expand $ISAIDR 81 */ 82 #if !defined(__i386) 83 /* ARGSUSED */ 84 #endif /* __i386 */ 85 static size_t 86 barg_isadir_var(char *var, int sz) 87 { 88 #if defined(__i386) 89 if (cpuid_64bit_capable() == 1) 90 return (strlcpy(var, "amd64", sz)); 91 #endif /* __i386 */ 92 93 var[0] = 0; 94 return (0); 95 } 96 97 /* 98 * Expand $ZFS-BOOTFS 99 */ 100 static size_t 101 barg_bootfs_var(const grub_barg_t *barg, char *var, int sz) 102 { 103 int n; 104 105 assert(barg); 106 if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) == 0) { 107 n = snprintf(var, sz, "zfs-bootfs=%s,bootpath=\"%s\"", 108 barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, 109 barg->gb_root.gr_physpath); 110 } else { 111 var[0] = 0; 112 n = 0; 113 } 114 return (n); 115 } 116 117 /* 118 * Expand all the variables without appending them more than once. 119 */ 120 static int 121 expand_var(char *arg, size_t argsz, const char *var, size_t varsz, 122 char *val, size_t valsz) 123 { 124 char *sp = arg; 125 size_t sz = argsz, len; 126 char *buf, *dst, *src; 127 int ret = 0; 128 129 buf = alloca(argsz); 130 dst = buf; 131 132 while ((src = strstr(sp, var)) != NULL) { 133 134 len = src - sp; 135 136 if (len + valsz > sz) { 137 ret = E2BIG; 138 break; 139 } 140 141 (void) bcopy(sp, dst, len); 142 (void) bcopy(val, dst + len, valsz); 143 dst += len + valsz; 144 sz -= len + valsz; 145 sp = src + varsz; 146 } 147 148 if (strlcpy(dst, sp, sz) >= sz) 149 ret = E2BIG; 150 151 if (ret == 0) 152 bcopy(buf, arg, argsz); 153 return (ret); 154 } 155 156 static int 157 match_bootfs(zfs_handle_t *zfh, void *data) 158 { 159 int ret; 160 const char *zfn; 161 grub_barg_t *barg = (grub_barg_t *)data; 162 163 ret = (zfs_get_type(zfh) == ZFS_TYPE_FILESYSTEM && 164 (zfn = zfs_get_name(zfh)) != NULL && 165 strcmp(barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, zfn) == 0); 166 167 if (ret != 0) 168 barg->gb_walkret = 0; 169 else 170 (void) zfs_iter_filesystems(zfh, match_bootfs, barg); 171 172 zfs_close(zfh); 173 return (barg->gb_walkret == 0); 174 } 175 176 static void 177 reset_root(grub_barg_t *barg) 178 { 179 (void) memset(&barg->gb_root, 0, sizeof (barg->gb_root)); 180 barg->gb_bootsign[0] = 0; 181 barg->gb_kernel[0] = 0; 182 RESET_MODULE(barg); 183 } 184 185 /* ARGSUSED */ 186 int 187 skip_line(const grub_line_t *lp, grub_barg_t *barg) 188 { 189 return (0); 190 } 191 192 /* ARGSUSED */ 193 int 194 error_line(const grub_line_t *lp, grub_barg_t *barg) 195 { 196 if (lp->gl_cmdtp == GRBM_ROOT_CMD) 197 return (EG_ROOTNOTSUPP); 198 return (EG_INVALIDLINE); 199 } 200 201 int 202 kernel(const grub_line_t *lp, grub_barg_t *barg) 203 { 204 RESET_MODULE(barg); 205 if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 206 sizeof (barg->gb_kernel)) 207 return (E2BIG); 208 209 return (0); 210 } 211 212 int 213 module(const grub_line_t *lp, grub_barg_t *barg) 214 { 215 if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 216 sizeof (barg->gb_module)) 217 return (E2BIG); 218 219 return (0); 220 } 221 222 int 223 dollar_kernel(const grub_line_t *lp, grub_barg_t *barg) 224 { 225 int ret; 226 size_t bfslen, isalen; 227 char isadir[32]; 228 char bootfs[BOOTARGS_MAX]; 229 230 RESET_MODULE(barg); 231 if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 232 sizeof (barg->gb_kernel)) 233 return (E2BIG); 234 235 bfslen = barg_bootfs_var(barg, bootfs, sizeof (bootfs)); 236 isalen = barg_isadir_var(isadir, sizeof (isadir)); 237 238 if (bfslen >= sizeof (bootfs) || isalen >= sizeof (isadir)) 239 return (EINVAL); 240 241 if ((ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 242 ZFS_BOOT_VAR, strlen(ZFS_BOOT_VAR), bootfs, bfslen)) != 0) 243 return (ret); 244 245 ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 246 ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 247 248 return (ret); 249 } 250 251 int 252 dollar_module(const grub_line_t *lp, grub_barg_t *barg) 253 { 254 int ret; 255 size_t isalen; 256 char isadir[32]; 257 258 if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 259 sizeof (barg->gb_module)) 260 return (E2BIG); 261 262 if ((isalen = barg_isadir_var(isadir, sizeof (isadir))) >= sizeof 263 (isadir)) 264 return (EINVAL); 265 266 ret = expand_var(barg->gb_module, sizeof (barg->gb_module), 267 ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 268 269 return (ret); 270 } 271 272 273 int 274 findroot(const grub_line_t *lp, grub_barg_t *barg) 275 { 276 size_t sz, bsz; 277 const char *sign; 278 279 reset_root(barg); 280 281 sign = lp->gl_arg; 282 barg->gb_prtnum = (uint_t)PRTNUM_INVALID; 283 barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; 284 285 if (sign[0] == '(') { 286 const char *pos; 287 288 ++sign; 289 if ((pos = strchr(sign, ',')) == NULL || (sz = pos - sign) == 0) 290 return (EG_FINDROOTFMT); 291 292 ++pos; 293 if (!IS_PRTNUM_VALID(barg->gb_prtnum = pos[0] - '0')) 294 return (EG_FINDROOTFMT); 295 296 ++pos; 297 if (pos[0] != ',' || 298 !IS_SLCNUM_VALID(barg->gb_slcnum = pos[1]) || 299 pos[2] != ')') 300 return (EG_FINDROOTFMT); 301 } else { 302 sz = strlen(sign); 303 } 304 305 bsz = strlen(BOOTSIGN_DIR "/"); 306 if (bsz + sz + 1 > sizeof (barg->gb_bootsign)) 307 return (E2BIG); 308 309 bcopy(BOOTSIGN_DIR "/", barg->gb_bootsign, bsz); 310 bcopy(sign, barg->gb_bootsign + bsz, sz); 311 barg->gb_bootsign [bsz + sz] = 0; 312 313 return (grub_find_bootsign(barg)); 314 } 315 316 int 317 bootfs(const grub_line_t *lp, grub_barg_t *barg) 318 { 319 zfs_handle_t *zfh; 320 grub_menu_t *mp = barg->gb_entry->ge_menu; 321 char *gfs_devp; 322 size_t gfs_dev_len; 323 324 /* Check if root is zfs */ 325 if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) != 0) 326 return (EG_NOTZFS); 327 328 gfs_devp = barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev; 329 gfs_dev_len = sizeof (barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev); 330 331 /* 332 * If the bootfs value is the same as the bootfs for the pool, 333 * do nothing. 334 */ 335 if (strcmp(lp->gl_arg, gfs_devp) == 0) 336 return (0); 337 338 if (strlcpy(gfs_devp, lp->gl_arg, gfs_dev_len) >= gfs_dev_len) 339 return (E2BIG); 340 341 /* check if specified bootfs belongs to the root pool */ 342 if ((zfh = zfs_open(mp->gm_fs.gf_lzfh, 343 barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_dev, 344 ZFS_TYPE_FILESYSTEM)) == NULL) 345 return (EG_OPENZFS); 346 347 barg->gb_walkret = EG_UNKBOOTFS; 348 (void) zfs_iter_filesystems(zfh, match_bootfs, barg); 349 zfs_close(zfh); 350 351 if (barg->gb_walkret == 0) 352 (void) grub_fsd_get_mountp(barg->gb_root.gr_fs + 353 GRBM_ZFS_BOOTFS, MNTTYPE_ZFS); 354 355 return (barg->gb_walkret); 356 } 357