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