1/* $NetBSD: biosboot.c,v 1.32 2019/06/20 10:56:38 martin 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.32 2019/06/20 10:56:38 martin Exp $"); 41#endif 42 43#include <sys/stat.h> 44#include <sys/types.h> 45#include <sys/ioctl.h> 46#include <sys/param.h> 47#include <sys/bootblock.h> 48 49#if defined(DIOCGWEDGEINFO) && !defined(HAVE_NBTOOL_CONFIG_H) 50#define USE_WEDGES 51#endif 52#ifdef USE_WEDGES 53#include <sys/disk.h> 54#endif 55 56#include <err.h> 57#include <fcntl.h> 58#include <paths.h> 59#include <stddef.h> 60#include <stdio.h> 61#include <stdlib.h> 62#include <string.h> 63#include <unistd.h> 64 65#include "map.h" 66#include "gpt.h" 67#include "gpt_private.h" 68 69#define DEFAULT_BOOTDIR "/usr/mdec" 70#define DEFAULT_BOOTCODE "gptmbr.bin" 71 72static int cmd_biosboot(gpt_t, int, char *[]); 73 74static const char *biosboothelp[] = { 75 "[-A] [-c bootcode] [-i index] [-L label] [-b startsec]", 76#if notyet 77 "[-a alignment] [-b blocknr] [-i index] [-l label]", 78 "[-s size] [-t type]", 79#endif 80}; 81 82struct gpt_cmd c_biosboot = { 83 "biosboot", 84 cmd_biosboot, 85 biosboothelp, __arraycount(biosboothelp), 86 0, 87}; 88 89#define usage() gpt_usage(NULL, &c_biosboot) 90 91static struct mbr* 92read_boot(gpt_t gpt, const char *bootpath) 93{ 94 int bfd, ret = -1; 95 struct mbr *buf; 96 struct stat st; 97 char *bp; 98 99 buf = NULL; 100 bfd = -1; 101 102 if (bootpath == NULL) 103 bp = strdup(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE); 104 else if (*bootpath == '/') 105 bp = strdup(bootpath); 106 else { 107 if (asprintf(&bp, "%s/%s", DEFAULT_BOOTDIR, bootpath) < 0) 108 bp = NULL; 109 } 110 111 if (bp == NULL) { 112 gpt_warn(gpt, "Can't allocate memory for bootpath"); 113 goto fail; 114 } 115 116 if ((buf = malloc((size_t)gpt->secsz)) == NULL) { 117 gpt_warn(gpt, "Can't allocate memory for sector"); 118 goto fail; 119 } 120 121 122 if ((bfd = open(bp, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) { 123 gpt_warn(gpt, "Can't open `%s'", bp); 124 goto fail; 125 } 126 127 if (st.st_size != MBR_DSN_OFFSET) { 128 gpt_warnx(gpt, "The bootcode in `%s' does not match the" 129 " expected size %u", bp, MBR_DSN_OFFSET); 130 goto fail; 131 } 132 133 if (read(bfd, buf, (size_t)st.st_size) != (ssize_t)st.st_size) { 134 gpt_warn(gpt, "Error reading from `%s'", bp); 135 goto fail; 136 } 137 138 ret = 0; 139fail: 140 if (bfd != -1) 141 close(bfd); 142 if (ret == -1) { 143 free(buf); 144 buf = NULL; 145 } 146 free(bp); 147 return buf; 148} 149 150static int 151set_bootable(gpt_t gpt, map_t map, map_t tbl, unsigned int i) 152{ 153 unsigned int j; 154 struct gpt_hdr *hdr = map->map_data; 155 struct gpt_ent *ent; 156 unsigned int ne = le32toh(hdr->hdr_entries); 157 158 for (j = 0; j < ne; j++) { 159 ent = gpt_ent(map, tbl, j); 160 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 161 } 162 163 ent = gpt_ent(map, tbl, i); 164 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 165 166 return gpt_write_crc(gpt, map, tbl); 167} 168 169static int 170biosboot(gpt_t gpt, daddr_t start, uint64_t size, u_int entry, uint8_t *label, 171 const char *bootpath, int active) 172{ 173 map_t mbrmap, m; 174 struct mbr *mbr, *bootcode; 175 unsigned int i; 176 struct gpt_ent *ent; 177 uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1]; 178 179 /* 180 * Parse and validate partition maps 181 */ 182 if (gpt_hdr(gpt) == NULL) 183 return -1; 184 185 mbrmap = map_find(gpt, MAP_TYPE_PMBR); 186 if (mbrmap == NULL || mbrmap->map_start != 0) { 187 gpt_warnx(gpt, "No valid Protective MBR found"); 188 return -1; 189 } 190 191 mbr = mbrmap->map_data; 192 193 /* 194 * Update the boot code 195 */ 196 if ((bootcode = read_boot(gpt, bootpath)) == NULL) { 197 gpt_warnx(gpt, "Error reading bootcode"); 198 return -1; 199 } 200 (void)memcpy(&mbr->mbr_code, &bootcode->mbr_code, 201 sizeof(mbr->mbr_code)); 202 free(bootcode); 203 204 for (i = 0; i < __arraycount(mbr->mbr_part); i++) 205 if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) 206 mbr->mbr_part[i].part_flag = active ? 0x80 : 0; 207 208 /* 209 * Walk through the GPT and see where we can boot from 210 */ 211 for (m = map_first(gpt); m != NULL; m = m->map_next) { 212 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) 213 continue; 214 215 ent = m->map_data; 216 217 /* first, prefer user selection */ 218 if (entry > 0 && m->map_index == entry) 219 break; 220 221 if (label != NULL) { 222 utf16_to_utf8(ent->ent_name, 223 __arraycount(ent->ent_name), utfbuf, 224 __arraycount(utfbuf)); 225 if (strcmp((char *)label, (char *)utfbuf) == 0) 226 break; 227 } 228 229 /* next, partition as could be specified by wedge */ 230 if (entry < 1 && label == NULL && size > 0 && 231 m->map_start == start && m->map_size == (off_t)size) 232 break; 233 /* next could be start sector specified by -b option */ 234 if (entry < 1 && label == NULL && size == 0 && 235 m->map_start == start) 236 break; 237 } 238 239 if (m == NULL) { 240 gpt_warnx(gpt, "No bootable partition"); 241 return -1; 242 } 243 244 i = m->map_index - 1; 245 246 247 if (set_bootable(gpt, gpt->gpt, gpt->tbl, i) == -1) 248 return -1; 249 250 if (set_bootable(gpt, gpt->tpg, gpt->lbt, i) == -1) 251 return -1; 252 253 if (gpt_write(gpt, mbrmap) == -1) { 254 gpt_warnx(gpt, "Cannot update Protective MBR"); 255 return -1; 256 } 257 258 gpt_msg(gpt, "Partition %d marked as bootable", i + 1); 259 return 0; 260} 261 262static int 263cmd_biosboot(gpt_t gpt, int argc, char *argv[]) 264{ 265#ifdef USE_WEDGES 266 struct dkwedge_info dkw; 267#endif 268 int ch; 269 gpt_t ngpt = gpt; 270 daddr_t start = 0; 271 uint64_t size = 0; 272 int active = 0; 273 unsigned int entry = 0; 274 uint8_t *label = NULL; 275 char *bootpath = NULL; 276 277 while ((ch = getopt(argc, argv, "Ac:i:L:b:")) != -1) { 278 switch(ch) { 279 case 'A': 280 active = 1; 281 break; 282 case 'c': 283 if (gpt_name_get(gpt, &bootpath) == -1) 284 goto usage; 285 break; 286 case 'i': 287 if (gpt_uint_get(gpt, &entry) == -1) 288 goto usage; 289 break; 290 case 'L': 291 if (gpt_name_get(gpt, &label) == -1) 292 goto usage; 293 break; 294 case 'b': 295 if (gpt_human_get(gpt, &start) == -1) 296 goto usage; 297 break; 298 default: 299 goto usage; 300 } 301 } 302 303 if (argc != optind) 304 return usage(); 305 306#ifdef USE_WEDGES 307 if ((gpt->sb.st_mode & S_IFMT) != S_IFREG && 308 ioctl(gpt->fd, DIOCGWEDGEINFO, &dkw) != -1) { 309 if (entry > 0) 310 /* wedges and indexes are mutually exclusive */ 311 goto usage; 312 start = dkw.dkw_offset; 313 size = dkw.dkw_size; 314 ngpt = gpt_open(dkw.dkw_parent, gpt->flags, gpt->verbose, 315 0, 0, 0); 316 if (ngpt == NULL) 317 goto cleanup; 318 } 319#endif 320 if (biosboot(ngpt, start, size, entry, label, bootpath, active) == -1) 321 goto cleanup; 322 if (ngpt != gpt) 323 gpt_close(ngpt); 324 return 0; 325usage: 326 usage(); 327cleanup: 328 if (ngpt != gpt) 329 gpt_close(ngpt); 330 free(bootpath); 331 free(label); 332 return -1; 333} 334