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 functions for constructing boot arguments 28 * from GRUB menu for Fast Reboot. 29 */ 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <errno.h> 33 #include <strings.h> 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <assert.h> 37 #include <sys/types.h> 38 #include <sys/elf.h> 39 40 #include "libgrub_impl.h" 41 42 #if defined(__sparc) 43 #define CUR_ELFDATA ELFDATA2MSB 44 #elif defined(__i386) 45 #define CUR_ELFDATA ELFDATA2LSB 46 #endif /* __i386 */ 47 48 /* 49 * Open the kernel file. 50 * Return zero on sucess or error code otherwise. 51 * On success the kernel file descriptor is returned in fdp. 52 */ 53 static int 54 get_kernel_fd(const char *path, int *fdp) 55 { 56 const char *bname; 57 int fd = -1, class, format; 58 char ident[EI_NIDENT]; 59 60 /* kernel basename must be unix */ 61 if ((bname = strrchr(path, '/')) == NULL) 62 bname = path; 63 else 64 bname++; 65 66 if (strcmp(bname, "unix") != 0) { 67 if (strcmp(bname, "xen.gz") == 0) 68 return (EG_XVMNOTSUP); 69 return (EG_NOTUNIX); 70 } 71 72 if ((fd = open64(path, O_RDONLY)) >= 0 && 73 (pread64(fd, ident, sizeof (ident), 0) == sizeof (ident))) { 74 75 class = ident[EI_CLASS]; 76 format = ident[EI_DATA]; 77 78 if ((class == ELFCLASS32 || class == ELFCLASS64) && 79 (memcmp(&ident[EI_MAG0], ELFMAG, 4) == 0) && 80 format == CUR_ELFDATA) { 81 *fdp = fd; 82 return (0); 83 } 84 } 85 86 if (fd >= 0) 87 (void) close(fd); 88 return (EG_OPENKERNFILE); 89 } 90 91 /* 92 * Construct boot arguments for Fast Reboot from the ge_barg field of 93 * a GRUB menu entry. 94 * Return 0 on success, errno on failure. 95 */ 96 static int 97 barg2bootargs(const grub_barg_t *barg, grub_boot_args_t *fbarg) 98 { 99 int rc = 0; 100 char path[BOOTARGS_MAX]; 101 char rpath[BOOTARGS_MAX]; 102 const grub_fsdesc_t *fsd; 103 104 assert(fbarg); 105 bzero(fbarg, sizeof (*fbarg)); 106 fbarg->gba_kernel_fd = -1; 107 108 if (!IS_BARG_VALID(barg)) 109 return (EINVAL); 110 if ((fsd = grub_get_rootfsd(&barg->gb_root)) == NULL) 111 return (EG_UNKNOWNFS); 112 113 bcopy(fsd, &fbarg->gba_fsd, sizeof (fbarg->gba_fsd)); 114 bcopy(barg->gb_kernel, fbarg->gba_kernel, sizeof (fbarg->gba_kernel)); 115 bcopy(barg->gb_module, fbarg->gba_module, sizeof (fbarg->gba_module)); 116 117 if (fbarg->gba_fsd.gfs_mountp[0] == 0 && 118 (rc = grub_fsd_mount_tmp(&fbarg->gba_fsd, 119 barg->gb_root.gr_fstyp)) != 0) 120 return (rc); 121 122 if (snprintf(path, sizeof (path), "%s%s", fbarg->gba_fsd.gfs_mountp, 123 fbarg->gba_kernel) >= sizeof (path)) { 124 rc = E2BIG; 125 goto err_out; 126 } 127 (void) strtok(path, " \t"); 128 (void) clean_path(path); 129 130 /* 131 * GRUB requires absolute path, no symlinks, so do we 132 */ 133 if ((rc = resolvepath(path, rpath, sizeof (rpath))) == -1) 134 rc = errno; 135 else { 136 rpath[rc] = 0; 137 if (strcmp(rpath, path) != 0) 138 rc = EG_NOTABSPATH; 139 else 140 rc = get_kernel_fd(rpath, &fbarg->gba_kernel_fd); 141 } 142 143 /* construct bootargs command-line */ 144 if (rc == 0 && snprintf(fbarg->gba_bootargs, 145 sizeof (fbarg->gba_bootargs), "%s %s", fbarg->gba_fsd.gfs_mountp, 146 fbarg->gba_kernel) >= sizeof (fbarg->gba_bootargs)) 147 rc = E2BIG; 148 149 err_out: 150 if (rc != 0) 151 grub_cleanup_boot_args(fbarg); 152 153 return (rc); 154 } 155 156 /* 157 * Construct boot arguments for Fast Reboot from grub_menu_t. 158 * Return 0 on success, errno on failure. 159 */ 160 static int 161 grub_entry_get_boot_args(grub_entry_t *ent, grub_boot_args_t *fbarg) 162 { 163 int rc = EG_INVALIDENT; 164 165 if (IS_ENTRY_VALID(ent) && (rc = grub_entry_construct_barg(ent)) == 0) 166 return (barg2bootargs(&ent->ge_barg, fbarg)); 167 else 168 return (rc); 169 } 170 171 /* 172 * Construct boot arguments for Fast Reboot from grub_menu_t and the 173 * entry number. 174 * Return 0 on success, errno on failure. 175 */ 176 static int 177 grub_menu_get_boot_args(const grub_menu_t *mp, int num, 178 grub_boot_args_t *fbarg) 179 { 180 grub_entry_t *ent; 181 182 assert(mp); 183 assert(fbarg); 184 185 if ((ent = grub_menu_get_entry(mp, num)) == NULL) 186 return (EG_NOENTRY); 187 188 return (grub_entry_get_boot_args(ent, fbarg)); 189 } 190 191 /* 192 * Construct boot arguments from the specified GRUB menu entry. 193 * Caller must allocate space for fbarg, and call grub_cleanup_boot_args() 194 * when it's done with fbarg to clean up. 195 * 196 * Return 0 on success, errno on failure. 197 */ 198 int 199 grub_get_boot_args(grub_boot_args_t *fbarg, const char *menupath, int num) 200 { 201 int rc; 202 grub_menu_t *mp; 203 204 assert(fbarg); 205 if ((rc = grub_menu_init(menupath, &mp)) == 0) { 206 rc = grub_menu_get_boot_args(mp, num, fbarg); 207 grub_menu_fini(mp); 208 } 209 return (rc); 210 } 211 212 /* 213 * Clean up when done with fbarg: close file handle, unmount file 214 * systems. Must be safe to call even if not all the fields are 215 * set up. 216 */ 217 void 218 grub_cleanup_boot_args(grub_boot_args_t *fbarg) 219 { 220 if (fbarg == NULL) 221 return; 222 223 (void) close(fbarg->gba_kernel_fd); 224 grub_fsd_umount_tmp(&fbarg->gba_fsd); 225 } 226