1 /* $NetBSD: biosboot.c,v 1.26 2016/06/09 19:04:43 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to the NetBSD Foundation 8 * by Mike M. Volokhov. Development of this software was supported by the 9 * Google Summer of Code program. 10 * The GSoC project was mentored by Allen Briggs and Joerg Sonnenberger. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #if HAVE_NBTOOL_CONFIG_H 35 #include "nbtool_config.h" 36 #endif 37 38 #include <sys/cdefs.h> 39 #ifdef __RCSID 40 __RCSID("$NetBSD: biosboot.c,v 1.26 2016/06/09 19:04:43 christos Exp $"); 41 #endif 42 43 #include <sys/stat.h> 44 #include <sys/types.h> 45 #include <sys/ioctl.h> 46 #ifdef DIOCGWEDGEINFO 47 #include <sys/disk.h> 48 #endif 49 #include <sys/param.h> 50 #include <sys/bootblock.h> 51 52 #include <err.h> 53 #include <fcntl.h> 54 #include <paths.h> 55 #include <stddef.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #include "map.h" 62 #include "gpt.h" 63 #include "gpt_private.h" 64 65 #define DEFAULT_BOOTDIR "/usr/mdec" 66 #define DEFAULT_BOOTCODE "gptmbr.bin" 67 68 static int cmd_biosboot(gpt_t, int, char *[]); 69 70 static const char *biosboothelp[] = { 71 "[-A] [-c bootcode] [-i index] [-L label]", 72 #if notyet 73 "[-a alignment] [-b blocknr] [-i index] [-l label]", 74 "[-s size] [-t type]", 75 #endif 76 }; 77 78 struct gpt_cmd c_biosboot = { 79 "biosboot", 80 cmd_biosboot, 81 biosboothelp, __arraycount(biosboothelp), 82 0, 83 }; 84 85 #define usage() gpt_usage(NULL, &c_biosboot) 86 87 static struct mbr* 88 read_boot(gpt_t gpt, const char *bootpath) 89 { 90 int bfd, ret = -1; 91 struct mbr *buf; 92 struct stat st; 93 char *bp; 94 95 buf = NULL; 96 bfd = -1; 97 98 if (bootpath == NULL) 99 bp = strdup(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE); 100 else if (*bootpath == '/') 101 bp = strdup(bootpath); 102 else { 103 if (asprintf(&bp, "%s/%s", DEFAULT_BOOTDIR, bootpath) < 0) 104 bp = NULL; 105 } 106 107 if (bp == NULL) { 108 gpt_warn(gpt, "Can't allocate memory for bootpath"); 109 goto fail; 110 } 111 112 if ((buf = malloc((size_t)gpt->secsz)) == NULL) { 113 gpt_warn(gpt, "Can't allocate memory for sector"); 114 goto fail; 115 } 116 117 118 if ((bfd = open(bp, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) { 119 gpt_warn(gpt, "Can't open `%s'", bp); 120 goto fail; 121 } 122 123 if (st.st_size != MBR_DSN_OFFSET) { 124 gpt_warnx(gpt, "The bootcode in `%s' does not match the" 125 " expected size %u", bp, MBR_DSN_OFFSET); 126 goto fail; 127 } 128 129 if (read(bfd, buf, (size_t)st.st_size) != (ssize_t)st.st_size) { 130 gpt_warn(gpt, "Error reading from `%s'", bp); 131 goto fail; 132 } 133 134 ret = 0; 135 fail: 136 if (bfd != -1) 137 close(bfd); 138 if (ret == -1) { 139 free(buf); 140 buf = NULL; 141 } 142 free(bp); 143 return buf; 144 } 145 146 static int 147 set_bootable(gpt_t gpt, map_t map, map_t tbl, unsigned int i) 148 { 149 unsigned int j; 150 struct gpt_hdr *hdr = map->map_data; 151 struct gpt_ent *ent; 152 unsigned int ne = le32toh(hdr->hdr_entries); 153 154 for (j = 0; j < ne; j++) { 155 ent = gpt_ent(map, tbl, j); 156 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 157 } 158 159 ent = gpt_ent(map, tbl, i); 160 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 161 162 return gpt_write_crc(gpt, map, tbl); 163 } 164 165 static int 166 biosboot(gpt_t gpt, daddr_t start, uint64_t size, u_int entry, uint8_t *label, 167 const char *bootpath, int active) 168 { 169 map_t mbrmap, m; 170 struct mbr *mbr, *bootcode; 171 unsigned int i; 172 struct gpt_ent *ent; 173 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1]; 174 175 /* 176 * Parse and validate partition maps 177 */ 178 if (gpt_hdr(gpt) == NULL) 179 return -1; 180 181 mbrmap = map_find(gpt, MAP_TYPE_PMBR); 182 if (mbrmap == NULL || mbrmap->map_start != 0) { 183 gpt_warnx(gpt, "No valid Protective MBR found"); 184 return -1; 185 } 186 187 mbr = mbrmap->map_data; 188 189 /* 190 * Update the boot code 191 */ 192 if ((bootcode = read_boot(gpt, bootpath)) == NULL) { 193 gpt_warnx(gpt, "Error reading bootcode"); 194 return -1; 195 } 196 (void)memcpy(&mbr->mbr_code, &bootcode->mbr_code, 197 sizeof(mbr->mbr_code)); 198 free(bootcode); 199 200 for (i = 0; i < __arraycount(mbr->mbr_part); i++) 201 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) 202 mbr->mbr_part[i].part_flag = active ? 0x80 : 0; 203 204 /* 205 * Walk through the GPT and see where we can boot from 206 */ 207 for (m = map_first(gpt); m != NULL; m = m->map_next) { 208 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) 209 continue; 210 211 ent = m->map_data; 212 213 /* first, prefer user selection */ 214 if (entry > 0 && m->map_index == entry) 215 break; 216 217 if (label != NULL) { 218 utf16_to_utf8(ent->ent_name, utfbuf, sizeof(utfbuf)); 219 if (strcmp((char *)label, (char *)utfbuf) == 0) 220 break; 221 } 222 223 /* next, partition as could be specified by wedge */ 224 if (entry < 1 && label == NULL && size > 0 && 225 m->map_start == start && m->map_size == (off_t)size) 226 break; 227 } 228 229 if (m == NULL) { 230 gpt_warnx(gpt, "No bootable partition"); 231 return -1; 232 } 233 234 i = m->map_index - 1; 235 236 237 if (set_bootable(gpt, gpt->gpt, gpt->tbl, i) == -1) 238 return -1; 239 240 if (set_bootable(gpt, gpt->tpg, gpt->lbt, i) == -1) 241 return -1; 242 243 if (gpt_write(gpt, mbrmap) == -1) { 244 gpt_warnx(gpt, "Cannot update Protective MBR"); 245 return -1; 246 } 247 248 gpt_msg(gpt, "Partition %d marked as bootable", i + 1); 249 return 0; 250 } 251 252 static int 253 cmd_biosboot(gpt_t gpt, int argc, char *argv[]) 254 { 255 #ifdef DIOCGWEDGEINFO 256 struct dkwedge_info dkw; 257 #endif 258 int ch; 259 gpt_t ngpt = gpt; 260 daddr_t start = 0; 261 uint64_t size = 0; 262 int active = 0; 263 unsigned int entry = 0; 264 uint8_t *label = NULL; 265 char *bootpath = NULL; 266 267 while ((ch = getopt(argc, argv, "Ac:i:L:")) != -1) { 268 switch(ch) { 269 case 'A': 270 active = 1; 271 break; 272 case 'c': 273 if (gpt_name_get(gpt, &bootpath) == -1) 274 goto usage; 275 break; 276 case 'i': 277 if (gpt_uint_get(gpt, &entry) == -1) 278 goto usage; 279 break; 280 case 'L': 281 if (gpt_name_get(gpt, &label) == -1) 282 goto usage; 283 break; 284 default: 285 goto usage; 286 } 287 } 288 289 if (argc != optind) 290 return usage(); 291 292 #ifdef DIOCGWEDGEINFO 293 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG && 294 ioctl(gpt->fd, DIOCGWEDGEINFO, &dkw) != -1) { 295 if (entry > 0) 296 /* wedges and indexes are mutually exclusive */ 297 goto usage; 298 start = dkw.dkw_offset; 299 size = dkw.dkw_size; 300 ngpt = gpt_open(dkw.dkw_parent, gpt->flags, gpt->verbose, 301 gpt->mediasz, gpt->secsz); 302 if (ngpt == NULL) 303 goto cleanup; 304 } 305 #endif 306 if (biosboot(ngpt, start, size, entry, label, bootpath, active) == -1) 307 goto cleanup; 308 if (ngpt != gpt) 309 gpt_close(ngpt); 310 return 0; 311 usage: 312 usage(); 313 cleanup: 314 if (ngpt != gpt) 315 gpt_close(ngpt); 316 free(bootpath); 317 free(label); 318 return -1; 319 } 320