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