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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2015 Nexenta Systems, Inc.
30 * This file contains all the functions that implement the following
32 * kernel, kernel$, module, module$, findroot, bootfs
33 * Return 0 on success, errno on failure.
43 #include <sys/types.h>
44 #include <sys/fs/ufs_mount.h>
45 #include <sys/dktp/fdisk.h>
47 #include <sys/x86_archext.h>
50 #include "libgrub_impl.h"
52 #define RESET_MODULE(barg) ((barg)->gb_module[0] = 0)
54 #define BPROP_ZFSBOOTFS "zfs-bootfs"
55 #define BPROP_BOOTPATH "bootpath"
58 static const char cpuid_dev
[] = "/dev/cpu/self/cpuid";
61 * Return 1 if the system supports 64-bit mode, 0 if it doesn't,
65 cpuid_64bit_capable(void)
69 uint32_t cp_eax
, cp_ebx
, cp_ecx
, cp_edx
;
72 if ((fd
= open(cpuid_dev
, O_RDONLY
)) == -1)
75 if (pread(fd
, &cpuid_regs
, sizeof (cpuid_regs
), 0x80000001) ==
77 ret
= ((CPUID_AMD_EDX_LM
& cpuid_regs
.cp_edx
) != 0);
92 barg_isadir_var(char *var
, int sz
)
95 if (cpuid_64bit_capable() == 1)
96 return (strlcpy(var
, "amd64", sz
));
107 barg_bootfs_var(const grub_barg_t
*barg
, char *var
, int sz
)
112 if (strcmp(barg
->gb_root
.gr_fstyp
, MNTTYPE_ZFS
) == 0) {
113 n
= snprintf(var
, sz
,
114 BPROP_ZFSBOOTFS
"=%s," BPROP_BOOTPATH
"=\"%s\"",
115 barg
->gb_root
.gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
,
116 barg
->gb_root
.gr_physpath
);
125 * Expand all the variables without appending them more than once.
128 expand_var(char *arg
, size_t argsz
, const char *var
, size_t varsz
,
129 char *val
, size_t valsz
)
132 size_t sz
= argsz
, len
;
133 char *buf
, *dst
, *src
;
139 while ((src
= strstr(sp
, var
)) != NULL
) {
143 if (len
+ valsz
> sz
) {
148 (void) bcopy(sp
, dst
, len
);
149 (void) bcopy(val
, dst
+ len
, valsz
);
155 if (strlcpy(dst
, sp
, sz
) >= sz
)
159 bcopy(buf
, arg
, argsz
);
164 * Searches first occurence of boot-property 'bprop' in str.
165 * str supposed to be in format:
166 * " [-B prop=[value][,prop=[value]]...]
169 find_bootprop(const char *str
, const char *bprop
)
177 bplen
= strlen(bprop
);
180 while ((str
= strstr(s
, " -B")) != NULL
||
181 (str
= strstr(s
, "\t-B")) != NULL
) {
183 len
= strspn(s
, " \t");
185 /* empty -B option, skip it */
186 if (len
!= 0 && s
[len
] == '-')
191 len
= strcspn(s
, "= \t");
195 /* boot property we are looking for? */
196 if (len
== bplen
&& strncmp(s
, bprop
, bplen
) == 0)
201 /* skip boot property value */
202 while ((s
= strpbrk(s
+ 1, "\"\', \t")) != NULL
) {
205 if (s
[0] == '\"' || s
[0] == '\'') {
206 if ((s
= strchr(s
+ 1, s
[0])) == NULL
) {
207 /* unbalanced quotes */
215 /* no more boot properties */
219 /* no more boot properties in that -B block */
224 } while (s
[0] != ' ' && s
[0] != '\t');
230 * Add bootpath property to str if
231 * 1. zfs-bootfs property is set explicitly
233 * 2. bootpath property is not set
236 update_bootpath(char *str
, size_t strsz
, const char *bootpath
)
242 /* zfs-bootfs is not specified, or bootpath is allready set */
243 if ((bfs
= find_bootprop(str
, BPROP_ZFSBOOTFS
)) == NULL
||
244 find_bootprop(str
, BPROP_BOOTPATH
) != NULL
)
251 if (snprintf(buf
+ n
, strsz
- n
, BPROP_BOOTPATH
"=\"%s\",%s",
252 bootpath
, bfs
) >= strsz
- n
)
255 bcopy(buf
, str
, strsz
);
260 match_bootfs(zfs_handle_t
*zfh
, void *data
)
264 grub_barg_t
*barg
= (grub_barg_t
*)data
;
266 ret
= (zfs_get_type(zfh
) == ZFS_TYPE_FILESYSTEM
&&
267 (zfn
= zfs_get_name(zfh
)) != NULL
&&
268 strcmp(barg
->gb_root
.gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
, zfn
) == 0);
271 barg
->gb_walkret
= 0;
273 (void) zfs_iter_filesystems(zfh
, match_bootfs
, barg
);
276 return (barg
->gb_walkret
== 0);
280 reset_root(grub_barg_t
*barg
)
282 (void) memset(&barg
->gb_root
, 0, sizeof (barg
->gb_root
));
283 barg
->gb_bootsign
[0] = 0;
284 barg
->gb_kernel
[0] = 0;
290 skip_line(const grub_line_t
*lp
, grub_barg_t
*barg
)
297 error_line(const grub_line_t
*lp
, grub_barg_t
*barg
)
299 if (lp
->gl_cmdtp
== GRBM_ROOT_CMD
)
300 return (EG_ROOTNOTSUPP
);
301 return (EG_INVALIDLINE
);
305 kernel(const grub_line_t
*lp
, grub_barg_t
*barg
)
308 if (strlcpy(barg
->gb_kernel
, lp
->gl_arg
, sizeof (barg
->gb_kernel
)) >=
309 sizeof (barg
->gb_kernel
))
316 module(const grub_line_t
*lp
, grub_barg_t
*barg
)
318 if (strlcpy(barg
->gb_module
, lp
->gl_arg
, sizeof (barg
->gb_module
)) >=
319 sizeof (barg
->gb_module
))
326 dollar_kernel(const grub_line_t
*lp
, grub_barg_t
*barg
)
329 size_t bfslen
, isalen
;
331 char bootfs
[BOOTARGS_MAX
];
334 if (strlcpy(barg
->gb_kernel
, lp
->gl_arg
, sizeof (barg
->gb_kernel
)) >=
335 sizeof (barg
->gb_kernel
))
338 bfslen
= barg_bootfs_var(barg
, bootfs
, sizeof (bootfs
));
339 isalen
= barg_isadir_var(isadir
, sizeof (isadir
));
341 if (bfslen
>= sizeof (bootfs
) || isalen
>= sizeof (isadir
))
344 if ((ret
= expand_var(barg
->gb_kernel
, sizeof (barg
->gb_kernel
),
345 ZFS_BOOT_VAR
, strlen(ZFS_BOOT_VAR
), bootfs
, bfslen
)) != 0)
348 if ((ret
= expand_var(barg
->gb_kernel
, sizeof (barg
->gb_kernel
),
349 ISADIR_VAR
, strlen(ISADIR_VAR
), isadir
, isalen
)) != 0)
352 if (strcmp(barg
->gb_root
.gr_fstyp
, MNTTYPE_ZFS
) == 0)
353 ret
= update_bootpath(barg
->gb_kernel
, sizeof (barg
->gb_kernel
),
354 barg
->gb_root
.gr_physpath
);
360 dollar_module(const grub_line_t
*lp
, grub_barg_t
*barg
)
366 if (strlcpy(barg
->gb_module
, lp
->gl_arg
, sizeof (barg
->gb_module
)) >=
367 sizeof (barg
->gb_module
))
370 if ((isalen
= barg_isadir_var(isadir
, sizeof (isadir
))) >= sizeof
374 ret
= expand_var(barg
->gb_module
, sizeof (barg
->gb_module
),
375 ISADIR_VAR
, strlen(ISADIR_VAR
), isadir
, isalen
);
382 findroot(const grub_line_t
*lp
, grub_barg_t
*barg
)
390 barg
->gb_prtnum
= (uint_t
)PRTNUM_INVALID
;
391 barg
->gb_slcnum
= (uint_t
)SLCNUM_WHOLE_DISK
;
393 if (sign
[0] == '(') {
397 if ((pos
= strchr(sign
, ',')) == NULL
|| (sz
= pos
- sign
) == 0)
398 return (EG_FINDROOTFMT
);
401 if (!IS_PRTNUM_VALID(barg
->gb_prtnum
= pos
[0] - '0'))
402 return (EG_FINDROOTFMT
);
406 * check the slice only when its presented
410 !IS_SLCNUM_VALID(barg
->gb_slcnum
= pos
[1]) ||
412 return (EG_FINDROOTFMT
);
418 bsz
= strlen(BOOTSIGN_DIR
"/");
419 if (bsz
+ sz
+ 1 > sizeof (barg
->gb_bootsign
))
422 bcopy(BOOTSIGN_DIR
"/", barg
->gb_bootsign
, bsz
);
423 bcopy(sign
, barg
->gb_bootsign
+ bsz
, sz
);
424 barg
->gb_bootsign
[bsz
+ sz
] = 0;
426 return (grub_find_bootsign(barg
));
430 bootfs(const grub_line_t
*lp
, grub_barg_t
*barg
)
433 grub_menu_t
*mp
= barg
->gb_entry
->ge_menu
;
437 /* Check if root is zfs */
438 if (strcmp(barg
->gb_root
.gr_fstyp
, MNTTYPE_ZFS
) != 0)
441 gfs_devp
= barg
->gb_root
.gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
;
442 gfs_dev_len
= sizeof (barg
->gb_root
.gr_fs
[GRBM_ZFS_BOOTFS
].gfs_dev
);
445 * If the bootfs value is the same as the bootfs for the pool,
448 if (strcmp(lp
->gl_arg
, gfs_devp
) == 0)
451 if (strlcpy(gfs_devp
, lp
->gl_arg
, gfs_dev_len
) >= gfs_dev_len
)
454 /* check if specified bootfs belongs to the root pool */
455 if ((zfh
= zfs_open(mp
->gm_fs
.gf_lzfh
,
456 barg
->gb_root
.gr_fs
[GRBM_ZFS_TOPFS
].gfs_dev
,
457 ZFS_TYPE_FILESYSTEM
)) == NULL
)
460 barg
->gb_walkret
= EG_UNKBOOTFS
;
461 (void) zfs_iter_filesystems(zfh
, match_bootfs
, barg
);
464 if (barg
->gb_walkret
== 0)
465 (void) grub_fsd_get_mountp(barg
->gb_root
.gr_fs
+
466 GRBM_ZFS_BOOTFS
, MNTTYPE_ZFS
);
468 return (barg
->gb_walkret
);