xref: /illumos-gate/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c (revision fda6624021ffe01adf1af44f72ae4fb3ba65c1e8)
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