Home | History | Annotate | Line # | Download | only in gpt
biosboot.c revision 1.16
      1 /*	$NetBSD: biosboot.c,v 1.16 2015/12/01 09:05:33 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.16 2015/12/01 09:05:33 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 daddr_t start;
     69 static uint64_t size;
     70 
     71 static char *bootpath;
     72 static unsigned int entry;
     73 static uint8_t *label;
     74 
     75 const char biosbootmsg[] = "biosboot [-c bootcode] [-i index] "
     76 	"[-L label]";
     77 
     78 static int
     79 usage_biosboot(void)
     80 {
     81 	fprintf(stderr, "usage: %s %s\n", getprogname(), biosbootmsg);
     82 	return -1;
     83 }
     84 
     85 static struct mbr*
     86 read_boot(gpt_t gpt)
     87 {
     88 	int bfd, ret = 0;
     89 	struct mbr *buf;
     90 	struct stat st;
     91 
     92 	buf = NULL;
     93 	bfd = -1;
     94 
     95 	if (bootpath == NULL)
     96 		bootpath = strdup(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE);
     97 	else if (*bootpath == '/')
     98 		bootpath = strdup(bootpath);
     99 	else {
    100 		if (asprintf(&bootpath, "%s/%s", DEFAULT_BOOTDIR, bootpath) < 0)
    101 			bootpath = NULL;
    102 	}
    103 
    104 	if (bootpath == NULL) {
    105 		gpt_warn(gpt, "Can't allocate memory for bootpath");
    106 		goto fail;
    107 	}
    108 
    109 	if ((buf = malloc((size_t)gpt->secsz)) == NULL) {
    110 		gpt_warn(gpt, "Can't allocate memory for sector");
    111 		goto fail;
    112 	}
    113 
    114 
    115 	if ((bfd = open(bootpath, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) {
    116 		gpt_warn(gpt, "Can't open `%s'", bootpath);
    117 		goto fail;
    118 	}
    119 
    120 	if (st.st_size != MBR_DSN_OFFSET) {
    121 		gpt_warnx(gpt, "The bootcode in `%s' does not match the"
    122 		    " expected size %u", bootpath, MBR_DSN_OFFSET);
    123 		goto fail;
    124 	}
    125 
    126 	if (read(bfd, buf, st.st_size) != st.st_size) {
    127 		gpt_warn(gpt, "Error reading from `%s'", bootpath);
    128 		goto fail;
    129 	}
    130 
    131 	ret++;
    132 
    133     fail:
    134 	if (bfd != -1)
    135 		close(bfd);
    136 	if (ret == 0) {
    137 		free(buf);
    138 		buf = NULL;
    139 	}
    140 	return buf;
    141 }
    142 
    143 static int
    144 set_bootable(gpt_t gpt, map_t map, map_t tbl, unsigned int i)
    145 {
    146 	unsigned int j;
    147 	struct gpt_hdr *hdr = map->map_data;
    148 	struct gpt_ent *ent;
    149 	unsigned int ne = le32toh(hdr->hdr_entries);
    150 
    151 	for (j = 0; j < ne; j++) {
    152 		ent = gpt_ent(map, tbl, j);
    153 		ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
    154 	}
    155 
    156 	ent = gpt_ent(map, tbl, i);
    157 	ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
    158 
    159 	return gpt_write_crc(gpt, map, tbl);
    160 }
    161 
    162 static int
    163 biosboot(gpt_t gpt)
    164 {
    165 	map_t mbrmap, m;
    166 	struct mbr *mbr, *bootcode;
    167 	unsigned int i;
    168 	struct gpt_ent *ent;
    169 
    170 	/*
    171 	 * Parse and validate partition maps
    172 	 */
    173 	if (gpt_hdr(gpt) == NULL)
    174 		return -1;
    175 
    176 	mbrmap = map_find(gpt, MAP_TYPE_PMBR);
    177 	if (mbrmap == NULL || mbrmap->map_start != 0) {
    178 		gpt_warnx(gpt, "No valid Protective MBR found");
    179 		return -1;
    180 	}
    181 
    182 	mbr = mbrmap->map_data;
    183 
    184 	/*
    185 	 * Update the boot code
    186 	 */
    187 	if ((bootcode = read_boot(gpt)) == NULL) {
    188 		gpt_warnx(gpt, "Error reading bootcode");
    189 		return -1;
    190 	}
    191 	(void)memcpy(&mbr->mbr_code, &bootcode->mbr_code,
    192 		sizeof(mbr->mbr_code));
    193 	free(bootcode);
    194 
    195 	/*
    196 	 * Walk through the GPT and see where we can boot from
    197 	 */
    198 	for (m = map_first(gpt); m != NULL; m = m->map_next) {
    199 		if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
    200 			continue;
    201 
    202 		ent = m->map_data;
    203 
    204 		/* first, prefer user selection */
    205 		if (entry > 0 && m->map_index == entry)
    206 			break;
    207 
    208 		if (label != NULL)
    209 			if (strcmp((char *)label,
    210 			    (char *)utf16_to_utf8(ent->ent_name)) == 0)
    211 				break;
    212 
    213 		/* next, partition as could be specified by wedge */
    214 		if (entry < 1 && label == NULL && size > 0 &&
    215 		    m->map_start == start && m->map_size == (off_t)size)
    216 			break;
    217 	}
    218 
    219 	if (m == NULL) {
    220 		gpt_warnx(gpt, "No bootable partition");
    221 		return -1;
    222 	}
    223 
    224 	i = m->map_index - 1;
    225 
    226 
    227 	if (set_bootable(gpt, gpt->gpt, gpt->tbl, i) == -1)
    228 		return -1;
    229 
    230 	if (set_bootable(gpt, gpt->tpg, gpt->lbt, i) == -1)
    231 		return -1;
    232 
    233 	if (gpt_write(gpt, mbrmap) == -1) {
    234 		gpt_warnx(gpt, "Cannot update Protective MBR");
    235 		return -1;
    236 	}
    237 
    238 	gpt_msg(gpt, "Partition %d marked as bootable", i + 1);
    239 	return 0;
    240 }
    241 
    242 int
    243 cmd_biosboot(gpt_t gpt, int argc, char *argv[])
    244 {
    245 #ifdef DIOCGWEDGEINFO
    246 	struct dkwedge_info dkw;
    247 #endif
    248 	char *dev, *p;
    249 	int ch;
    250 	gpt_t ngpt = gpt;
    251 
    252 	while ((ch = getopt(argc, argv, "c:i:L:")) != -1) {
    253 		switch(ch) {
    254 		case 'c':
    255 			if (bootpath != NULL)
    256 				usage_biosboot();
    257 			if ((bootpath = strdup(optarg)) == NULL)
    258 				err(1, "Malloc failed");
    259 			break;
    260 		case 'i':
    261 			if (entry > 0)
    262 				usage_biosboot();
    263 			entry = strtoul(optarg, &p, 10);
    264 			if (*p != 0 || entry < 1)
    265 				usage_biosboot();
    266 			break;
    267 		case 'L':
    268 			if (label != NULL)
    269 				usage_biosboot();
    270 			label = (uint8_t *)strdup(optarg);
    271 			break;
    272 		default:
    273 			return usage_biosboot();
    274 		}
    275 	}
    276 
    277 	if (argc != optind)
    278 		return usage_biosboot();
    279 
    280 	start = 0;
    281 	size = 0;
    282 
    283 #ifdef DIOCGWEDGEINFO
    284 	if ((gpt->sb.st_mode & S_IFMT) != S_IFREG &&
    285 	    ioctl(gpt->fd, DIOCGWEDGEINFO, &dkw) != -1) {
    286 		if (entry > 0)
    287 			/* wedges and indexes are mutually exclusive */
    288 			return usage_biosboot();
    289 		dev = dkw.dkw_parent;
    290 		start = dkw.dkw_offset;
    291 		size = dkw.dkw_size;
    292 		ngpt = gpt_open(dev, gpt->flags, gpt->verbose,
    293 		    gpt->mediasz, gpt->secsz);
    294 		if (ngpt == NULL)
    295 			return -1;
    296 	}
    297 #endif
    298 	biosboot(ngpt);
    299 	if (ngpt != gpt)
    300 		gpt_close(ngpt);
    301 
    302 	return 0;
    303 }
    304