]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sbin/gpt/boot.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sbin / gpt / boot.c
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$");
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
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;
52
53 static void
54 usage_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
63 static int
64 gpt_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
113 static void
114 boot(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
229 int
230 cmd_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 < 0) {
261                         warn("unable to open device '%s'", device_name);
262                         continue;
263                 }
264
265                 boot(fd);
266
267                 gpt_close(fd);
268         }
269
270         return (0);
271 }