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 return (EG_INVALIDLINE); 197 } 198 199 int 200 kernel(const grub_line_t *lp, grub_barg_t *barg) 201 { 202 RESET_MODULE(barg); 203 if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 204 sizeof (barg->gb_kernel)) 205 return (E2BIG); 206 207 return (0); 208 } 209 210 int 211 module(const grub_line_t *lp, grub_barg_t *barg) 212 { 213 if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 214 sizeof (barg->gb_module)) 215 return (E2BIG); 216 217 return (0); 218 } 219 220 int 221 dollar_kernel(const grub_line_t *lp, grub_barg_t *barg) 222 { 223 int ret; 224 size_t bfslen, isalen; 225 char isadir[32]; 226 char bootfs[BOOTARGS_MAX]; 227 228 RESET_MODULE(barg); 229 if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 230 sizeof (barg->gb_kernel)) 231 return (E2BIG); 232 233 bfslen = barg_bootfs_var(barg, bootfs, sizeof (bootfs)); 234 isalen = barg_isadir_var(isadir, sizeof (isadir)); 235 236 if (bfslen >= sizeof (bootfs) || isalen >= sizeof (isadir)) 237 return (EINVAL); 238 239 if ((ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 240 ZFS_BOOT_VAR, strlen(ZFS_BOOT_VAR), bootfs, bfslen)) != 0) 241 return (ret); 242 243 ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 244 ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 245 246 return (ret); 247 } 248 249 int 250 dollar_module(const grub_line_t *lp, grub_barg_t *barg) 251 { 252 int ret; 253 size_t isalen; 254 char isadir[32]; 255 256 if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 257 sizeof (barg->gb_module)) 258 return (E2BIG); 259 260 if ((isalen = barg_isadir_var(isadir, sizeof (isadir))) >= sizeof 261 (isadir)) 262 return (EINVAL); 263 264 ret = expand_var(barg->gb_module, sizeof (barg->gb_module), 265 ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 266 267 return (ret); 268 } 269 270 271 int 272 findroot(const grub_line_t *lp, grub_barg_t *barg) 273 { 274 size_t sz, bsz; 275 const char *sign; 276 277 reset_root(barg); 278 279 sign = lp->gl_arg; 280 barg->gb_prtnum = (uint_t)PRTNUM_INVALID; 281 barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; 282 283 if (sign[0] == '(') { 284 const char *pos; 285 286 ++sign; 287 if ((pos = strchr(sign, ',')) == NULL || (sz = pos - sign) == 0) 288 return (EG_FINDROOTFMT); 289 290 ++pos; 291 if (!IS_PRTNUM_VALID(barg->gb_prtnum = pos[0] - '0')) 292 return (EG_FINDROOTFMT); 293 294 ++pos; 295 if (pos[0] != ',' || 296 !IS_SLCNUM_VALID(barg->gb_slcnum = pos[1]) || 297 pos[2] != ')') 298 return (EG_FINDROOTFMT); 299 } else { 300 sz = strlen(sign); 301 } 302 303 bsz = strlen(BOOTSIGN_DIR "/"); 304 if (bsz + sz + 1 > sizeof (barg->gb_bootsign)) 305 return (E2BIG); 306 307 bcopy(BOOTSIGN_DIR "/", barg->gb_bootsign, bsz); 308 bcopy(sign, barg->gb_bootsign + bsz, sz); 309 barg->gb_bootsign [bsz + sz] = 0; 310 311 return (grub_find_bootsign(barg)); 312 } 313 314 int 315 bootfs(const grub_line_t *lp, grub_barg_t *barg) 316 { 317 zfs_handle_t *zfh; 318 grub_menu_t *mp = barg->gb_entry->ge_menu; 319 char *gfs_devp; 320 size_t gfs_dev_len; 321 322 /* Check if root is zfs */ 323 if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) != 0) 324 return (EG_NOTZFS); 325 326 gfs_devp = barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev; 327 gfs_dev_len = sizeof (barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev); 328 329 /* 330 * If the bootfs value is the same as the bootfs for the pool, 331 * do nothing. 332 */ 333 if (strcmp(lp->gl_arg, gfs_devp) == 0) 334 return (0); 335 336 if (strlcpy(gfs_devp, lp->gl_arg, gfs_dev_len) >= gfs_dev_len) 337 return (E2BIG); 338 339 /* check if specified bootfs belongs to the root pool */ 340 if ((zfh = zfs_open(mp->gm_fs.gf_lzfh, 341 barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_dev, 342 ZFS_TYPE_FILESYSTEM)) == NULL) 343 return (EG_OPENZFS); 344 345 barg->gb_walkret = EG_UNKBOOTFS; 346 (void) zfs_iter_filesystems(zfh, match_bootfs, barg); 347 zfs_close(zfh); 348 349 if (barg->gb_walkret == 0) 350 (void) grub_fsd_get_mountp(barg->gb_root.gr_fs + 351 GRBM_ZFS_BOOTFS, MNTTYPE_ZFS); 352 353 return (barg->gb_walkret); 354 } 355