1753a6d45SSherry Moore /* 2753a6d45SSherry Moore * CDDL HEADER START 3753a6d45SSherry Moore * 4753a6d45SSherry Moore * The contents of this file are subject to the terms of the 5753a6d45SSherry Moore * Common Development and Distribution License (the "License"). 6753a6d45SSherry Moore * You may not use this file except in compliance with the License. 7753a6d45SSherry Moore * 8753a6d45SSherry Moore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9753a6d45SSherry Moore * or http://www.opensolaris.org/os/licensing. 10753a6d45SSherry Moore * See the License for the specific language governing permissions 11753a6d45SSherry Moore * and limitations under the License. 12753a6d45SSherry Moore * 13753a6d45SSherry Moore * When distributing Covered Code, include this CDDL HEADER in each 14753a6d45SSherry Moore * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15753a6d45SSherry Moore * If applicable, add the following below this CDDL HEADER, with the 16753a6d45SSherry Moore * fields enclosed by brackets "[]" replaced with your own identifying 17753a6d45SSherry Moore * information: Portions Copyright [yyyy] [name of copyright owner] 18753a6d45SSherry Moore * 19753a6d45SSherry Moore * CDDL HEADER END 20753a6d45SSherry Moore */ 21753a6d45SSherry Moore /* 22753a6d45SSherry Moore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23753a6d45SSherry Moore * Use is subject to license terms. 24753a6d45SSherry Moore */ 25753a6d45SSherry Moore 26753a6d45SSherry Moore /* 27753a6d45SSherry Moore * This file contains all the functions that implement the following 28753a6d45SSherry Moore * GRUB commands: 29753a6d45SSherry Moore * kernel, kernel$, module, module$, findroot, bootfs 30753a6d45SSherry Moore * Return 0 on success, errno on failure. 31753a6d45SSherry Moore */ 32753a6d45SSherry Moore #include <stdio.h> 33753a6d45SSherry Moore #include <stdlib.h> 34753a6d45SSherry Moore #include <assert.h> 35753a6d45SSherry Moore #include <alloca.h> 36753a6d45SSherry Moore #include <errno.h> 37753a6d45SSherry Moore #include <strings.h> 38753a6d45SSherry Moore #include <unistd.h> 39753a6d45SSherry Moore #include <fcntl.h> 40753a6d45SSherry Moore #include <sys/types.h> 41753a6d45SSherry Moore #include <sys/fs/ufs_mount.h> 42753a6d45SSherry Moore #include <sys/dktp/fdisk.h> 43753a6d45SSherry Moore #if defined(__i386) 44753a6d45SSherry Moore #include <sys/x86_archext.h> 45753a6d45SSherry Moore #endif /* __i386 */ 46753a6d45SSherry Moore 47753a6d45SSherry Moore #include "libgrub_impl.h" 48753a6d45SSherry Moore 49753a6d45SSherry Moore #define RESET_MODULE(barg) ((barg)->gb_module[0] = 0) 50753a6d45SSherry Moore 51753a6d45SSherry Moore #if defined(__i386) 52753a6d45SSherry Moore static const char cpuid_dev[] = "/dev/cpu/self/cpuid"; 53753a6d45SSherry Moore 54753a6d45SSherry Moore /* 55753a6d45SSherry Moore * Return 1 if the system supports 64-bit mode, 0 if it doesn't, 56753a6d45SSherry Moore * or -1 on failure. 57753a6d45SSherry Moore */ 58753a6d45SSherry Moore static int 59753a6d45SSherry Moore cpuid_64bit_capable(void) 60753a6d45SSherry Moore { 61753a6d45SSherry Moore int fd, ret = -1; 62753a6d45SSherry Moore struct { 63753a6d45SSherry Moore uint32_t cp_eax, cp_ebx, cp_ecx, cp_edx; 64753a6d45SSherry Moore } cpuid_regs; 65753a6d45SSherry Moore 66753a6d45SSherry Moore if ((fd = open(cpuid_dev, O_RDONLY)) == -1) 67753a6d45SSherry Moore return (ret); 68753a6d45SSherry Moore 69753a6d45SSherry Moore if (pread(fd, &cpuid_regs, sizeof (cpuid_regs), 0x80000001) == 70753a6d45SSherry Moore sizeof (cpuid_regs)) 71753a6d45SSherry Moore ret = ((CPUID_AMD_EDX_LM & cpuid_regs.cp_edx) != 0); 72753a6d45SSherry Moore 73753a6d45SSherry Moore (void) close(fd); 74753a6d45SSherry Moore return (ret); 75753a6d45SSherry Moore } 76753a6d45SSherry Moore #endif /* __i386 */ 77753a6d45SSherry Moore 78753a6d45SSherry Moore 79753a6d45SSherry Moore /* 80753a6d45SSherry Moore * Expand $ISAIDR 81753a6d45SSherry Moore */ 82753a6d45SSherry Moore #if !defined(__i386) 83753a6d45SSherry Moore /* ARGSUSED */ 84753a6d45SSherry Moore #endif /* __i386 */ 85753a6d45SSherry Moore static size_t 86753a6d45SSherry Moore barg_isadir_var(char *var, int sz) 87753a6d45SSherry Moore { 88753a6d45SSherry Moore #if defined(__i386) 89753a6d45SSherry Moore if (cpuid_64bit_capable() == 1) 90753a6d45SSherry Moore return (strlcpy(var, "amd64", sz)); 91753a6d45SSherry Moore #endif /* __i386 */ 92753a6d45SSherry Moore 93753a6d45SSherry Moore var[0] = 0; 94753a6d45SSherry Moore return (0); 95753a6d45SSherry Moore } 96753a6d45SSherry Moore 97753a6d45SSherry Moore /* 98753a6d45SSherry Moore * Expand $ZFS-BOOTFS 99753a6d45SSherry Moore */ 100753a6d45SSherry Moore static size_t 101753a6d45SSherry Moore barg_bootfs_var(const grub_barg_t *barg, char *var, int sz) 102753a6d45SSherry Moore { 103753a6d45SSherry Moore int n; 104753a6d45SSherry Moore 105753a6d45SSherry Moore assert(barg); 106753a6d45SSherry Moore if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) == 0) { 107753a6d45SSherry Moore n = snprintf(var, sz, "zfs-bootfs=%s,bootpath=\"%s\"", 108753a6d45SSherry Moore barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, 109753a6d45SSherry Moore barg->gb_root.gr_physpath); 110753a6d45SSherry Moore } else { 111753a6d45SSherry Moore var[0] = 0; 112753a6d45SSherry Moore n = 0; 113753a6d45SSherry Moore } 114753a6d45SSherry Moore return (n); 115753a6d45SSherry Moore } 116753a6d45SSherry Moore 117753a6d45SSherry Moore /* 118753a6d45SSherry Moore * Expand all the variables without appending them more than once. 119753a6d45SSherry Moore */ 120753a6d45SSherry Moore static int 121753a6d45SSherry Moore expand_var(char *arg, size_t argsz, const char *var, size_t varsz, 122753a6d45SSherry Moore char *val, size_t valsz) 123753a6d45SSherry Moore { 124753a6d45SSherry Moore char *sp = arg; 125753a6d45SSherry Moore size_t sz = argsz, len; 126753a6d45SSherry Moore char *buf, *dst, *src; 127753a6d45SSherry Moore int ret = 0; 128753a6d45SSherry Moore 129753a6d45SSherry Moore buf = alloca(argsz); 130753a6d45SSherry Moore dst = buf; 131753a6d45SSherry Moore 132753a6d45SSherry Moore while ((src = strstr(sp, var)) != NULL) { 133753a6d45SSherry Moore 134753a6d45SSherry Moore len = src - sp; 135753a6d45SSherry Moore 136753a6d45SSherry Moore if (len + valsz > sz) { 137753a6d45SSherry Moore ret = E2BIG; 138753a6d45SSherry Moore break; 139753a6d45SSherry Moore } 140753a6d45SSherry Moore 141753a6d45SSherry Moore (void) bcopy(sp, dst, len); 142753a6d45SSherry Moore (void) bcopy(val, dst + len, valsz); 143753a6d45SSherry Moore dst += len + valsz; 144753a6d45SSherry Moore sz -= len + valsz; 145753a6d45SSherry Moore sp = src + varsz; 146753a6d45SSherry Moore } 147753a6d45SSherry Moore 148753a6d45SSherry Moore if (strlcpy(dst, sp, sz) >= sz) 149753a6d45SSherry Moore ret = E2BIG; 150753a6d45SSherry Moore 151753a6d45SSherry Moore if (ret == 0) 152753a6d45SSherry Moore bcopy(buf, arg, argsz); 153753a6d45SSherry Moore return (ret); 154753a6d45SSherry Moore } 155753a6d45SSherry Moore 156753a6d45SSherry Moore static int 157753a6d45SSherry Moore match_bootfs(zfs_handle_t *zfh, void *data) 158753a6d45SSherry Moore { 159753a6d45SSherry Moore int ret; 160753a6d45SSherry Moore const char *zfn; 161753a6d45SSherry Moore grub_barg_t *barg = (grub_barg_t *)data; 162753a6d45SSherry Moore 163753a6d45SSherry Moore ret = (zfs_get_type(zfh) == ZFS_TYPE_FILESYSTEM && 164753a6d45SSherry Moore (zfn = zfs_get_name(zfh)) != NULL && 165753a6d45SSherry Moore strcmp(barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev, zfn) == 0); 166753a6d45SSherry Moore 167753a6d45SSherry Moore if (ret != 0) 168753a6d45SSherry Moore barg->gb_walkret = 0; 169753a6d45SSherry Moore else 170753a6d45SSherry Moore (void) zfs_iter_filesystems(zfh, match_bootfs, barg); 171753a6d45SSherry Moore 172753a6d45SSherry Moore zfs_close(zfh); 173753a6d45SSherry Moore return (barg->gb_walkret == 0); 174753a6d45SSherry Moore } 175753a6d45SSherry Moore 176753a6d45SSherry Moore static void 177753a6d45SSherry Moore reset_root(grub_barg_t *barg) 178753a6d45SSherry Moore { 179753a6d45SSherry Moore (void) memset(&barg->gb_root, 0, sizeof (barg->gb_root)); 180753a6d45SSherry Moore barg->gb_bootsign[0] = 0; 181753a6d45SSherry Moore barg->gb_kernel[0] = 0; 182753a6d45SSherry Moore RESET_MODULE(barg); 183753a6d45SSherry Moore } 184753a6d45SSherry Moore 185753a6d45SSherry Moore /* ARGSUSED */ 186753a6d45SSherry Moore int 187753a6d45SSherry Moore skip_line(const grub_line_t *lp, grub_barg_t *barg) 188753a6d45SSherry Moore { 189753a6d45SSherry Moore return (0); 190753a6d45SSherry Moore } 191753a6d45SSherry Moore 192753a6d45SSherry Moore /* ARGSUSED */ 193753a6d45SSherry Moore int 194753a6d45SSherry Moore error_line(const grub_line_t *lp, grub_barg_t *barg) 195753a6d45SSherry Moore { 196*fda66240SKonstantin Ananyev if (lp->gl_cmdtp == GRBM_ROOT_CMD) 197*fda66240SKonstantin Ananyev return (EG_ROOTNOTSUPP); 198753a6d45SSherry Moore return (EG_INVALIDLINE); 199753a6d45SSherry Moore } 200753a6d45SSherry Moore 201753a6d45SSherry Moore int 202753a6d45SSherry Moore kernel(const grub_line_t *lp, grub_barg_t *barg) 203753a6d45SSherry Moore { 204753a6d45SSherry Moore RESET_MODULE(barg); 205753a6d45SSherry Moore if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 206753a6d45SSherry Moore sizeof (barg->gb_kernel)) 207753a6d45SSherry Moore return (E2BIG); 208753a6d45SSherry Moore 209753a6d45SSherry Moore return (0); 210753a6d45SSherry Moore } 211753a6d45SSherry Moore 212753a6d45SSherry Moore int 213753a6d45SSherry Moore module(const grub_line_t *lp, grub_barg_t *barg) 214753a6d45SSherry Moore { 215753a6d45SSherry Moore if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 216753a6d45SSherry Moore sizeof (barg->gb_module)) 217753a6d45SSherry Moore return (E2BIG); 218753a6d45SSherry Moore 219753a6d45SSherry Moore return (0); 220753a6d45SSherry Moore } 221753a6d45SSherry Moore 222753a6d45SSherry Moore int 223753a6d45SSherry Moore dollar_kernel(const grub_line_t *lp, grub_barg_t *barg) 224753a6d45SSherry Moore { 225753a6d45SSherry Moore int ret; 226753a6d45SSherry Moore size_t bfslen, isalen; 227753a6d45SSherry Moore char isadir[32]; 228753a6d45SSherry Moore char bootfs[BOOTARGS_MAX]; 229753a6d45SSherry Moore 230753a6d45SSherry Moore RESET_MODULE(barg); 231753a6d45SSherry Moore if (strlcpy(barg->gb_kernel, lp->gl_arg, sizeof (barg->gb_kernel)) >= 232753a6d45SSherry Moore sizeof (barg->gb_kernel)) 233753a6d45SSherry Moore return (E2BIG); 234753a6d45SSherry Moore 235753a6d45SSherry Moore bfslen = barg_bootfs_var(barg, bootfs, sizeof (bootfs)); 236753a6d45SSherry Moore isalen = barg_isadir_var(isadir, sizeof (isadir)); 237753a6d45SSherry Moore 238753a6d45SSherry Moore if (bfslen >= sizeof (bootfs) || isalen >= sizeof (isadir)) 239753a6d45SSherry Moore return (EINVAL); 240753a6d45SSherry Moore 241753a6d45SSherry Moore if ((ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 242753a6d45SSherry Moore ZFS_BOOT_VAR, strlen(ZFS_BOOT_VAR), bootfs, bfslen)) != 0) 243753a6d45SSherry Moore return (ret); 244753a6d45SSherry Moore 245753a6d45SSherry Moore ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel), 246753a6d45SSherry Moore ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 247753a6d45SSherry Moore 248753a6d45SSherry Moore return (ret); 249753a6d45SSherry Moore } 250753a6d45SSherry Moore 251753a6d45SSherry Moore int 252753a6d45SSherry Moore dollar_module(const grub_line_t *lp, grub_barg_t *barg) 253753a6d45SSherry Moore { 254753a6d45SSherry Moore int ret; 255753a6d45SSherry Moore size_t isalen; 256753a6d45SSherry Moore char isadir[32]; 257753a6d45SSherry Moore 258753a6d45SSherry Moore if (strlcpy(barg->gb_module, lp->gl_arg, sizeof (barg->gb_module)) >= 259753a6d45SSherry Moore sizeof (barg->gb_module)) 260753a6d45SSherry Moore return (E2BIG); 261753a6d45SSherry Moore 262753a6d45SSherry Moore if ((isalen = barg_isadir_var(isadir, sizeof (isadir))) >= sizeof 263753a6d45SSherry Moore (isadir)) 264753a6d45SSherry Moore return (EINVAL); 265753a6d45SSherry Moore 266753a6d45SSherry Moore ret = expand_var(barg->gb_module, sizeof (barg->gb_module), 267753a6d45SSherry Moore ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen); 268753a6d45SSherry Moore 269753a6d45SSherry Moore return (ret); 270753a6d45SSherry Moore } 271753a6d45SSherry Moore 272753a6d45SSherry Moore 273753a6d45SSherry Moore int 274753a6d45SSherry Moore findroot(const grub_line_t *lp, grub_barg_t *barg) 275753a6d45SSherry Moore { 276753a6d45SSherry Moore size_t sz, bsz; 277753a6d45SSherry Moore const char *sign; 278753a6d45SSherry Moore 279753a6d45SSherry Moore reset_root(barg); 280753a6d45SSherry Moore 281753a6d45SSherry Moore sign = lp->gl_arg; 282753a6d45SSherry Moore barg->gb_prtnum = (uint_t)PRTNUM_INVALID; 283753a6d45SSherry Moore barg->gb_slcnum = (uint_t)SLCNUM_WHOLE_DISK; 284753a6d45SSherry Moore 285753a6d45SSherry Moore if (sign[0] == '(') { 286753a6d45SSherry Moore const char *pos; 287753a6d45SSherry Moore 288753a6d45SSherry Moore ++sign; 289753a6d45SSherry Moore if ((pos = strchr(sign, ',')) == NULL || (sz = pos - sign) == 0) 290753a6d45SSherry Moore return (EG_FINDROOTFMT); 291753a6d45SSherry Moore 292753a6d45SSherry Moore ++pos; 293753a6d45SSherry Moore if (!IS_PRTNUM_VALID(barg->gb_prtnum = pos[0] - '0')) 294753a6d45SSherry Moore return (EG_FINDROOTFMT); 295753a6d45SSherry Moore 296753a6d45SSherry Moore ++pos; 297753a6d45SSherry Moore if (pos[0] != ',' || 298753a6d45SSherry Moore !IS_SLCNUM_VALID(barg->gb_slcnum = pos[1]) || 299753a6d45SSherry Moore pos[2] != ')') 300753a6d45SSherry Moore return (EG_FINDROOTFMT); 301753a6d45SSherry Moore } else { 302753a6d45SSherry Moore sz = strlen(sign); 303753a6d45SSherry Moore } 304753a6d45SSherry Moore 305753a6d45SSherry Moore bsz = strlen(BOOTSIGN_DIR "/"); 306753a6d45SSherry Moore if (bsz + sz + 1 > sizeof (barg->gb_bootsign)) 307753a6d45SSherry Moore return (E2BIG); 308753a6d45SSherry Moore 309753a6d45SSherry Moore bcopy(BOOTSIGN_DIR "/", barg->gb_bootsign, bsz); 310753a6d45SSherry Moore bcopy(sign, barg->gb_bootsign + bsz, sz); 311753a6d45SSherry Moore barg->gb_bootsign [bsz + sz] = 0; 312753a6d45SSherry Moore 313753a6d45SSherry Moore return (grub_find_bootsign(barg)); 314753a6d45SSherry Moore } 315753a6d45SSherry Moore 316753a6d45SSherry Moore int 317753a6d45SSherry Moore bootfs(const grub_line_t *lp, grub_barg_t *barg) 318753a6d45SSherry Moore { 319753a6d45SSherry Moore zfs_handle_t *zfh; 320753a6d45SSherry Moore grub_menu_t *mp = barg->gb_entry->ge_menu; 321753a6d45SSherry Moore char *gfs_devp; 322753a6d45SSherry Moore size_t gfs_dev_len; 323753a6d45SSherry Moore 324753a6d45SSherry Moore /* Check if root is zfs */ 325753a6d45SSherry Moore if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) != 0) 326753a6d45SSherry Moore return (EG_NOTZFS); 327753a6d45SSherry Moore 328753a6d45SSherry Moore gfs_devp = barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev; 329753a6d45SSherry Moore gfs_dev_len = sizeof (barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev); 330753a6d45SSherry Moore 331753a6d45SSherry Moore /* 332753a6d45SSherry Moore * If the bootfs value is the same as the bootfs for the pool, 333753a6d45SSherry Moore * do nothing. 334753a6d45SSherry Moore */ 335753a6d45SSherry Moore if (strcmp(lp->gl_arg, gfs_devp) == 0) 336753a6d45SSherry Moore return (0); 337753a6d45SSherry Moore 338753a6d45SSherry Moore if (strlcpy(gfs_devp, lp->gl_arg, gfs_dev_len) >= gfs_dev_len) 339753a6d45SSherry Moore return (E2BIG); 340753a6d45SSherry Moore 341753a6d45SSherry Moore /* check if specified bootfs belongs to the root pool */ 342753a6d45SSherry Moore if ((zfh = zfs_open(mp->gm_fs.gf_lzfh, 343753a6d45SSherry Moore barg->gb_root.gr_fs[GRBM_ZFS_TOPFS].gfs_dev, 344753a6d45SSherry Moore ZFS_TYPE_FILESYSTEM)) == NULL) 345753a6d45SSherry Moore return (EG_OPENZFS); 346753a6d45SSherry Moore 347753a6d45SSherry Moore barg->gb_walkret = EG_UNKBOOTFS; 348753a6d45SSherry Moore (void) zfs_iter_filesystems(zfh, match_bootfs, barg); 349753a6d45SSherry Moore zfs_close(zfh); 350753a6d45SSherry Moore 351753a6d45SSherry Moore if (barg->gb_walkret == 0) 352753a6d45SSherry Moore (void) grub_fsd_get_mountp(barg->gb_root.gr_fs + 353753a6d45SSherry Moore GRBM_ZFS_BOOTFS, MNTTYPE_ZFS); 354753a6d45SSherry Moore 355753a6d45SSherry Moore return (barg->gb_walkret); 356753a6d45SSherry Moore } 357