2 * Copyright (c) 2007 Yahoo!, Inc.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/types.h>
48 static uuid_t boot_uuid = GPT_ENT_TYPE_FREEBSD_BOOT;
49 static const char *pmbr_path = "/boot/pmbr";
50 static const char *gptboot_path = "/boot/gptboot";
51 static u_long boot_size;
58 "usage: %s [-b pmbr] [-g gptboot] [-s count] device ...\n",
64 gpt_find(uuid_t *type, map_t **mapp)
66 map_t *gpt, *tbl, *map;
71 /* Find a GPT partition with the requested UUID type. */
72 gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
74 warnx("%s: error: no primary GPT header", device_name);
78 tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
80 warnx("%s: error: no primary partition table", device_name);
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))
91 if (i == le32toh(hdr->hdr_entries)) {
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)
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));
109 /* Hmm, the map list is not in sync with the GPT table. */
110 errx(1, "internal map list is corrupted");
118 map_t *pmbr, *gptboot;
125 /* First step: verify boot partition size. */
127 /* Default to 64k. */
128 bsize = 65536 / secsz;
130 if (boot_size * secsz < 16384) {
131 warnx("invalid boot partition size %lu", boot_size);
137 /* Second step: write the PMBR boot loader into the PMBR. */
138 pmbr = map_find(MAP_TYPE_PMBR);
140 warnx("%s: error: PMBR not found", device_name);
143 bfd = open(pmbr_path, O_RDONLY);
144 if (bfd < 0 || fstat(bfd, &sb) < 0) {
145 warn("unable to open PMBR boot loader");
148 if (sb.st_size != secsz) {
149 warnx("invalid PMBR boot loader");
152 mbr = pmbr->map_data;
153 nbytes = read(bfd, mbr->mbr_code, sizeof(mbr->mbr_code));
155 warn("unable to read PMBR boot loader");
158 if (nbytes != sizeof(mbr->mbr_code)) {
159 warnx("short read of PMBR boot loader");
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");
172 /* Fourth step: find an existing boot partition or create one. */
173 if (gpt_find(&boot_uuid, &gptboot) != 0)
175 if (gptboot != NULL) {
176 if (gptboot->map_size * secsz < sb.st_size) {
177 warnx("%s: error: boot partition is too small",
181 } else if (bsize * secsz < sb.st_size) {
183 "%s: error: proposed size for boot partition is too small",
188 gptboot = gpt_add_part(fd, boot_uuid, 0, bsize, &entry);
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.
199 bsize = (sb.st_size + secsz - 1) / secsz * secsz;
200 buf = calloc(1, bsize);
201 nbytes = read(bfd, buf, sb.st_size);
203 warn("unable to read GPT boot loader");
206 if (nbytes != sb.st_size) {
207 warnx("short read of GPT boot loader");
211 ofs = gptboot->map_start * secsz;
212 if (lseek(fd, ofs, SEEK_SET) != ofs) {
213 warn("%s: error: unable to seek to boot partition",
217 nbytes = write(fd, buf, bsize);
219 warn("unable to write GPT boot loader");
222 if (nbytes != bsize) {
223 warnx("short write of GPT boot loader");
230 cmd_boot(int argc, char *argv[])
235 while ((ch = getopt(argc, argv, "b:g:s:")) != -1) {
241 gptboot_path = optarg;
246 boot_size = strtol(optarg, &p, 10);
247 if (*p != '\0' || boot_size < 1)
258 while (optind < argc) {
259 fd = gpt_open(argv[optind++]);
261 warn("unable to open device '%s'", device_name);