1/* $NetBSD: biosboot.c,v 1.4 2011/08/17 12:27:50 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#include <sys/cdefs.h> 35#ifdef __RCSID 36__RCSID("$NetBSD: biosboot.c,v 1.4 2011/08/17 12:27:50 martin Exp $"); 37#endif 38 39#include <sys/stat.h> 40#include <sys/types.h> 41#include <sys/ioctl.h> 42#include <sys/disk.h> 43#include <sys/param.h> 44#include <sys/bootblock.h> 45 46#include <err.h> 47#include <fcntl.h> 48#include <inttypes.h> 49#include <paths.h> 50#include <stddef.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <unistd.h> 55#include <util.h> 56 57#include "map.h" 58#include "gpt.h" 59 60#define DEFAULT_BOOTDIR "/usr/mdec" 61#define DEFAULT_BOOTCODE "gptmbr.bin" 62 63static daddr_t start; 64static uint64_t size; 65 66static char *bootpath; 67static unsigned int entry; 68 69const char biosbootmsg[] = "biosboot [-c bootcode] [-i index] device ..."; 70 71__dead static void 72usage_biosboot(void) 73{ 74 fprintf(stderr, "usage: %s %s\n", getprogname(), biosbootmsg); 75 exit(1); 76} 77 78static struct mbr* 79read_boot(void) 80{ 81 int bfd, ret = 0; 82 struct mbr *buf; 83 struct stat st; 84 85 /* XXX how to do the following better? */ 86 if (bootpath == NULL) { 87 bootpath = strdup(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE); 88 } else { 89 if (strchr(bootpath, '/') == 0) { 90 char *p; 91 if ((p = strdup(bootpath)) == NULL) 92 err(1, "Malloc failed"); 93 free(bootpath); 94 (void)asprintf(&bootpath, "%s/%s", DEFAULT_BOOTDIR, p); 95 free(p); 96 } 97 } 98 if (bootpath == NULL) 99 err(1, "Malloc failed"); 100 101 if ((buf = malloc((size_t)secsz)) == NULL) 102 err(1, "Malloc failed"); 103 104 if ((bfd = open(bootpath, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) { 105 warn("%s", bootpath); 106 goto fail; 107 } 108 109 if (st.st_size != MBR_DSN_OFFSET) { 110 warnx("%s: the bootcode does not match expected size", 111 bootpath); 112 goto fail; 113 } 114 115 if (read(bfd, buf, st.st_size) != st.st_size) { 116 warn("%s", bootpath); 117 goto fail; 118 } 119 120 ret++; 121 122 fail: 123 if (bfd >= 0) 124 close(bfd); 125 if (ret == 0) { 126 free(buf); 127 buf = NULL; 128 } 129 return buf; 130} 131 132static void 133biosboot(int fd) 134{ 135 map_t *gpt, *tpg; 136 map_t *tbl, *lbt; 137 map_t *mbrmap, *m; 138 struct mbr *mbr, *bootcode; 139 struct gpt_hdr *hdr; 140 struct gpt_ent *ent; 141 int i; 142 143 /* 144 * Parse and validate partition maps 145 */ 146 gpt = map_find(MAP_TYPE_PRI_GPT_HDR); 147 if (gpt == NULL) { 148 warnx("%s: error: no primary GPT header; run create or recover", 149 device_name); 150 return; 151 } 152 153 tpg = map_find(MAP_TYPE_SEC_GPT_HDR); 154 if (tpg == NULL) { 155 warnx("%s: error: no secondary GPT header; run recover", 156 device_name); 157 return; 158 } 159 160 tbl = map_find(MAP_TYPE_PRI_GPT_TBL); 161 lbt = map_find(MAP_TYPE_SEC_GPT_TBL); 162 if (tbl == NULL || lbt == NULL) { 163 warnx("%s: error: run recover -- trust me", device_name); 164 return; 165 } 166 167 mbrmap = map_find(MAP_TYPE_PMBR); 168 if (mbrmap == NULL || mbrmap->map_start != 0) { 169 warnx("%s: error: no valid Protective MBR found", device_name); 170 return; 171 } 172 173 mbr = mbrmap->map_data; 174 175 /* 176 * Update the boot code 177 */ 178 if ((bootcode = read_boot()) == NULL) { 179 warnx("error reading bootcode"); 180 return; 181 } 182 (void)memcpy(&mbr->mbr_code, &bootcode->mbr_code, 183 sizeof(mbr->mbr_code)); 184 free(bootcode); 185 186 /* 187 * Walk through the GPT and see where we can boot from 188 */ 189 for (m = map_first(); m != NULL; m = m->map_next) { 190 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) 191 continue; 192 193 ent = m->map_data; 194 195 /* first, prefer user selection */ 196 if (entry > 0 && m->map_index == entry) 197 break; 198 199 /* next, partition as could be specified by wedge */ 200 if (entry < 1 && size > 0 && 201 m->map_start == start && m->map_size == (off_t)size) 202 break; 203 } 204 205 if (m == NULL) { 206 warnx("error: no bootable partition"); 207 return; 208 } 209 210 i = m->map_index - 1; 211 212 213 hdr = gpt->map_data; 214 215 for (uint32_t j = 0; j < le32toh(hdr->hdr_entries); j++) { 216 ent = (void*)((char*)tbl->map_data + j * le32toh(hdr->hdr_entsz)); 217 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 218 } 219 220 ent = (void*)((char*)tbl->map_data + i * le32toh(hdr->hdr_entsz)); 221 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 222 223 hdr->hdr_crc_table = htole32(crc32(tbl->map_data, 224 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); 225 hdr->hdr_crc_self = 0; 226 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 227 228 gpt_write(fd, gpt); 229 gpt_write(fd, tbl); 230 231 232 hdr = tpg->map_data; 233 234 for (uint32_t j = 0; j < le32toh(hdr->hdr_entries); j++) { 235 ent = (void*)((char*)lbt->map_data + j * le32toh(hdr->hdr_entsz)); 236 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 237 } 238 239 ent = (void*)((char*)lbt->map_data + i * le32toh(hdr->hdr_entsz)); 240 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE; 241 242 hdr->hdr_crc_table = htole32(crc32(lbt->map_data, 243 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); 244 hdr->hdr_crc_self = 0; 245 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 246 247 gpt_write(fd, lbt); 248 gpt_write(fd, tpg); 249 250 251 if (gpt_write(fd, mbrmap) == -1) { 252 warnx("error: cannot update Protective MBR"); 253 return; 254 } 255} 256 257int 258cmd_biosboot(int argc, char *argv[]) 259{ 260 struct dkwedge_info dkw; 261 struct stat sb; 262 char devpath[MAXPATHLEN]; 263 char *dev, *p; 264 int ch, fd; 265 266 while ((ch = getopt(argc, argv, "c:i:")) != -1) { 267 switch(ch) { 268 case 'c': 269 if (bootpath != NULL) 270 usage_biosboot(); 271 if ((bootpath = strdup(optarg)) == NULL) 272 err(1, "Malloc failed"); 273 break; 274 case 'i': 275 if (entry > 0) 276 usage_biosboot(); 277 entry = strtoul(optarg, &p, 10); 278 if (*p != 0 || entry < 1) 279 usage_biosboot(); 280 break; 281 default: 282 usage_biosboot(); 283 } 284 } 285 286 if (argc == optind) 287 usage_biosboot(); 288 289 while (optind < argc) { 290 dev = argv[optind++]; 291 start = 0; 292 size = 0; 293 294#ifdef __NetBSD__ 295 /* 296 * If a dk wedge was specified, loader should be 297 * installed onto parent device 298 */ 299 if ((fd = opendisk(dev, O_RDONLY, devpath, sizeof(devpath), 0)) 300 == -1) 301 goto next; 302 if (fstat(fd, &sb) == -1) 303 goto close; 304 305#ifdef DIOCGWEDGEINFO 306 if ((sb.st_mode & S_IFMT) != S_IFREG && 307 ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) { 308 if (entry > 0) 309 /* wedges and indexes are mutually exclusive */ 310 usage_biosboot(); 311 dev = dkw.dkw_parent; 312 start = dkw.dkw_offset; 313 size = dkw.dkw_size; 314 } 315#endif 316 close: 317 close(fd); 318#endif /* __NetBSD__*/ 319 320 fd = gpt_open(dev); 321 next: 322 if (fd == -1) { 323 warn("unable to open device '%s'", device_name); 324 continue; 325 } 326 327 biosboot(fd); 328 329 gpt_close(fd); 330 } 331 332 return (0); 333} 334