1/*- 2 * Copyright (c) 2007 Yahoo!, Inc. 3 * All rights reserved. 4 * Written by: John Baldwin <jhb@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the author nor the names of any co-contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: src/sbin/gpt/boot.c,v 1.2.2.1.6.1 2010/02/10 00:26:20 kensmith Exp $"); 33 34#include <sys/types.h> 35#include <sys/stat.h> 36 37#include <assert.h> 38#include <err.h> 39#include <errno.h> 40#include <fcntl.h> 41#include <unistd.h> 42#include <stdio.h> 43#include <stdlib.h> 44 45#include "map.h" 46#include "gpt.h" 47 48static uuid_t boot_uuid = GPT_ENT_TYPE_FREEBSD_BOOT; 49static const char *pmbr_path = "/boot/pmbr"; 50static const char *gptboot_path = "/boot/gptboot"; 51static u_long boot_size; 52 53static void 54usage_boot(void) 55{ 56 57 fprintf(stderr, 58 "usage: %s [-b pmbr] [-g gptboot] [-s count] device ...\n", 59 getprogname()); 60 exit(1); 61} 62 63static int 64gpt_find(uuid_t *type, map_t **mapp) 65{ 66 map_t *gpt, *tbl, *map; 67 struct gpt_hdr *hdr; 68 struct gpt_ent *ent; 69 unsigned int i; 70 71 /* Find a GPT partition with the requested UUID type. */ 72 gpt = map_find(MAP_TYPE_PRI_GPT_HDR); 73 if (gpt == NULL) { 74 warnx("%s: error: no primary GPT header", device_name); 75 return (ENXIO); 76 } 77 78 tbl = map_find(MAP_TYPE_PRI_GPT_TBL); 79 if (tbl == NULL) { 80 warnx("%s: error: no primary partition table", device_name); 81 return (ENXIO); 82 } 83 84 hdr = gpt->map_data; 85 for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 86 ent = (void *)((char *)tbl->map_data + i * 87 le32toh(hdr->hdr_entsz)); 88 if (uuid_equal(&ent->ent_type, type, NULL)) 89 break; 90 } 91 if (i == le32toh(hdr->hdr_entries)) { 92 *mapp = NULL; 93 return (0); 94 } 95 96 /* Lookup the map corresponding to this partition. */ 97 for (map = map_find(MAP_TYPE_GPT_PART); map != NULL; 98 map = map->map_next) { 99 if (map->map_type != MAP_TYPE_GPT_PART) 100 continue; 101 if (map->map_start == (off_t)le64toh(ent->ent_lba_start)) { 102 assert(map->map_start + map->map_size - 1LL == 103 (off_t)le64toh(ent->ent_lba_end)); 104 *mapp = map; 105 return (0); 106 } 107 } 108 109 /* Hmm, the map list is not in sync with the GPT table. */ 110 errx(1, "internal map list is corrupted"); 111} 112 113static void 114boot(int fd) 115{ 116 struct stat sb; 117 off_t bsize, ofs; 118 map_t *pmbr, *gptboot; 119 struct mbr *mbr; 120 char *buf; 121 ssize_t nbytes; 122 unsigned int entry; 123 int bfd; 124 125 /* First step: verify boot partition size. */ 126 if (boot_size == 0) 127 /* Default to 64k. */ 128 bsize = 65536 / secsz; 129 else { 130 if (boot_size * secsz < 16384) { 131 warnx("invalid boot partition size %lu", boot_size); 132 return; 133 } 134 bsize = boot_size; 135 } 136 137 /* Second step: write the PMBR boot loader into the PMBR. */ 138 pmbr = map_find(MAP_TYPE_PMBR); 139 if (pmbr == NULL) { 140 warnx("%s: error: PMBR not found", device_name); 141 return; 142 } 143 bfd = open(pmbr_path, O_RDONLY); 144 if (bfd < 0 || fstat(bfd, &sb) < 0) { 145 warn("unable to open PMBR boot loader"); 146 return; 147 } 148 if (sb.st_size != secsz) { 149 warnx("invalid PMBR boot loader"); 150 return; 151 } 152 mbr = pmbr->map_data; 153 nbytes = read(bfd, mbr->mbr_code, sizeof(mbr->mbr_code)); 154 if (nbytes < 0) { 155 warn("unable to read PMBR boot loader"); 156 return; 157 } 158 if (nbytes != sizeof(mbr->mbr_code)) { 159 warnx("short read of PMBR boot loader"); 160 return; 161 } 162 close(bfd); 163 gpt_write(fd, pmbr); 164 165 /* Third step: open gptboot and obtain its size. */ 166 bfd = open(gptboot_path, O_RDONLY); 167 if (bfd < 0 || fstat(bfd, &sb) < 0) { 168 warn("unable to open GPT boot loader"); 169 return; 170 } 171 172 /* Fourth step: find an existing boot partition or create one. */ 173 if (gpt_find(&boot_uuid, &gptboot) != 0) 174 return; 175 if (gptboot != NULL) { 176 if (gptboot->map_size * secsz < sb.st_size) { 177 warnx("%s: error: boot partition is too small", 178 device_name); 179 return; 180 } 181 } else if (bsize * secsz < sb.st_size) { 182 warnx( 183 "%s: error: proposed size for boot partition is too small", 184 device_name); 185 return; 186 } else { 187 entry = 0; 188 gptboot = gpt_add_part(fd, &boot_uuid, 0, bsize, &entry); 189 if (gptboot == NULL) 190 return; 191 } 192 193 /* 194 * Fourth step, write out the gptboot binary to the boot partition. 195 * When writing to a disk device, the write must be sector aligned 196 * and not write to any partial sectors, so round up the buffer size 197 * to the next sector and zero it. 198 */ 199 bsize = (sb.st_size + secsz - 1) / secsz * secsz; 200 buf = calloc(1, bsize); 201 nbytes = read(bfd, buf, sb.st_size); 202 if (nbytes < 0) { 203 warn("unable to read GPT boot loader"); 204 return; 205 } 206 if (nbytes != sb.st_size) { 207 warnx("short read of GPT boot loader"); 208 return; 209 } 210 close(bfd); 211 ofs = gptboot->map_start * secsz; 212 if (lseek(fd, ofs, SEEK_SET) != ofs) { 213 warn("%s: error: unable to seek to boot partition", 214 device_name); 215 return; 216 } 217 nbytes = write(fd, buf, bsize); 218 if (nbytes < 0) { 219 warn("unable to write GPT boot loader"); 220 return; 221 } 222 if (nbytes != bsize) { 223 warnx("short write of GPT boot loader"); 224 return; 225 } 226 free(buf); 227} 228 229int 230cmd_boot(int argc, char *argv[]) 231{ 232 char *p; 233 int ch, fd; 234 235 while ((ch = getopt(argc, argv, "b:g:s:")) != -1) { 236 switch (ch) { 237 case 'b': 238 pmbr_path = optarg; 239 break; 240 case 'g': 241 gptboot_path = optarg; 242 break; 243 case 's': 244 if (boot_size > 0) 245 usage_boot(); 246 boot_size = strtol(optarg, &p, 10); 247 if (*p != '\0' || boot_size < 1) 248 usage_boot(); 249 break; 250 default: 251 usage_boot(); 252 } 253 } 254 255 if (argc == optind) 256 usage_boot(); 257 258 while (optind < argc) { 259 fd = gpt_open(argv[optind++]); 260 if (fd == -1) { 261 warn("unable to open device '%s'", device_name); 262 return (1); 263 } 264 265 boot(fd); 266 267 gpt_close(fd); 268 } 269 270 return (0); 271} 272