biosboot.c revision 1.13 1 /* $NetBSD: biosboot.c,v 1.13 2014/10/02 19:15:21 joerg 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 #if HAVE_NBTOOL_CONFIG_H
35 #include "nbtool_config.h"
36 #endif
37
38 #include <sys/cdefs.h>
39 #ifdef __RCSID
40 __RCSID("$NetBSD: biosboot.c,v 1.13 2014/10/02 19:15:21 joerg Exp $");
41 #endif
42
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/ioctl.h>
46 #ifndef HAVE_NBTOOL_CONFIG_H
47 #include <sys/disk.h>
48 #endif
49 #include <sys/param.h>
50 #include <sys/bootblock.h>
51
52 #include <err.h>
53 #include <fcntl.h>
54 #include <paths.h>
55 #include <stddef.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60
61 #include "map.h"
62 #include "gpt.h"
63
64 #define DEFAULT_BOOTDIR "/usr/mdec"
65 #define DEFAULT_BOOTCODE "gptmbr.bin"
66
67 static daddr_t start;
68 static uint64_t size;
69
70 static char *bootpath;
71 static unsigned int entry;
72 static uint8_t *label;
73
74 const char biosbootmsg[] = "biosboot [-c bootcode] [-i index] "
75 "[-L label] device ...";
76
77 __dead static void
78 usage_biosboot(void)
79 {
80 fprintf(stderr, "usage: %s %s\n", getprogname(), biosbootmsg);
81 exit(1);
82 }
83
84 static struct mbr*
85 read_boot(void)
86 {
87 int bfd, ret = 0;
88 struct mbr *buf;
89 struct stat st;
90
91 /* XXX how to do the following better? */
92 if (bootpath == NULL) {
93 bootpath = strdup(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE);
94 } else {
95 if (strchr(bootpath, '/') == 0) {
96 char *p;
97 if ((p = strdup(bootpath)) == NULL)
98 err(1, "Malloc failed");
99 free(bootpath);
100 (void)asprintf(&bootpath, "%s/%s", DEFAULT_BOOTDIR, p);
101 free(p);
102 }
103 }
104 if (bootpath == NULL)
105 err(1, "Malloc failed");
106
107 if ((buf = malloc((size_t)secsz)) == NULL)
108 err(1, "Malloc failed");
109
110 if ((bfd = open(bootpath, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) {
111 warn("%s", bootpath);
112 goto fail;
113 }
114
115 if (st.st_size != MBR_DSN_OFFSET) {
116 warnx("%s: the bootcode does not match expected size",
117 bootpath);
118 goto fail;
119 }
120
121 if (read(bfd, buf, st.st_size) != st.st_size) {
122 warn("%s", bootpath);
123 goto fail;
124 }
125
126 ret++;
127
128 fail:
129 if (bfd >= 0)
130 close(bfd);
131 if (ret == 0) {
132 free(buf);
133 buf = NULL;
134 }
135 return buf;
136 }
137
138 static void
139 biosboot(int fd)
140 {
141 map_t *gpt, *tpg;
142 map_t *tbl, *lbt;
143 map_t *mbrmap, *m;
144 struct mbr *mbr, *bootcode;
145 struct gpt_hdr *hdr;
146 struct gpt_ent *ent;
147 unsigned int i, j;
148
149 /*
150 * Parse and validate partition maps
151 */
152 gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
153 if (gpt == NULL) {
154 warnx("%s: error: no primary GPT header; run create or recover",
155 device_name);
156 return;
157 }
158
159 tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
160 if (tpg == NULL) {
161 warnx("%s: error: no secondary GPT header; run recover",
162 device_name);
163 return;
164 }
165
166 tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
167 lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
168 if (tbl == NULL || lbt == NULL) {
169 warnx("%s: error: run recover -- trust me", device_name);
170 return;
171 }
172
173 mbrmap = map_find(MAP_TYPE_PMBR);
174 if (mbrmap == NULL || mbrmap->map_start != 0) {
175 warnx("%s: error: no valid Protective MBR found", device_name);
176 return;
177 }
178
179 mbr = mbrmap->map_data;
180
181 /*
182 * Update the boot code
183 */
184 if ((bootcode = read_boot()) == NULL) {
185 warnx("error reading bootcode");
186 return;
187 }
188 (void)memcpy(&mbr->mbr_code, &bootcode->mbr_code,
189 sizeof(mbr->mbr_code));
190 free(bootcode);
191
192 /*
193 * Walk through the GPT and see where we can boot from
194 */
195 for (m = map_first(); m != NULL; m = m->map_next) {
196 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
197 continue;
198
199 ent = m->map_data;
200
201 /* first, prefer user selection */
202 if (entry > 0 && m->map_index == entry)
203 break;
204
205 if (label != NULL)
206 if (strcmp((char *)label,
207 (char *)utf16_to_utf8(ent->ent_name)) == 0)
208 break;
209
210 /* next, partition as could be specified by wedge */
211 if (entry < 1 && label == NULL && size > 0 &&
212 m->map_start == start && m->map_size == (off_t)size)
213 break;
214 }
215
216 if (m == NULL) {
217 warnx("error: no bootable partition");
218 return;
219 }
220
221 i = m->map_index - 1;
222
223
224 hdr = gpt->map_data;
225
226 for (j = 0; j < le32toh(hdr->hdr_entries); j++) {
227 ent = (void*)((char*)tbl->map_data + j * le32toh(hdr->hdr_entsz));
228 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
229 }
230
231 ent = (void*)((char*)tbl->map_data + i * le32toh(hdr->hdr_entsz));
232 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
233
234 hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
235 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
236 hdr->hdr_crc_self = 0;
237 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
238
239 gpt_write(fd, gpt);
240 gpt_write(fd, tbl);
241
242
243 hdr = tpg->map_data;
244
245 for (j = 0; j < le32toh(hdr->hdr_entries); j++) {
246 ent = (void*)((char*)lbt->map_data + j * le32toh(hdr->hdr_entsz));
247 ent->ent_attr &= ~GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
248 }
249
250 ent = (void*)((char*)lbt->map_data + i * le32toh(hdr->hdr_entsz));
251 ent->ent_attr |= GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE;
252
253 hdr->hdr_crc_table = htole32(crc32(lbt->map_data,
254 le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
255 hdr->hdr_crc_self = 0;
256 hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
257
258 gpt_write(fd, lbt);
259 gpt_write(fd, tpg);
260
261
262 if (gpt_write(fd, mbrmap) == -1) {
263 warnx("error: cannot update Protective MBR");
264 return;
265 }
266
267 printf("partition %d marked as bootable\n", i + 1);
268 }
269
270 int
271 cmd_biosboot(int argc, char *argv[])
272 {
273 #ifdef DIOCGWEDGEINFO
274 struct dkwedge_info dkw;
275 #endif
276 struct stat sb;
277 char devpath[MAXPATHLEN];
278 char *dev, *p;
279 int ch, fd;
280
281 while ((ch = getopt(argc, argv, "c:i:L:")) != -1) {
282 switch(ch) {
283 case 'c':
284 if (bootpath != NULL)
285 usage_biosboot();
286 if ((bootpath = strdup(optarg)) == NULL)
287 err(1, "Malloc failed");
288 break;
289 case 'i':
290 if (entry > 0)
291 usage_biosboot();
292 entry = strtoul(optarg, &p, 10);
293 if (*p != 0 || entry < 1)
294 usage_biosboot();
295 break;
296 case 'L':
297 if (label != NULL)
298 usage_biosboot();
299 label = (uint8_t *)strdup(optarg);
300 break;
301 default:
302 usage_biosboot();
303 }
304 }
305
306 if (argc == optind)
307 usage_biosboot();
308
309 while (optind < argc) {
310 dev = argv[optind++];
311 start = 0;
312 size = 0;
313
314 /*
315 * If a dk wedge was specified, loader should be
316 * installed onto parent device
317 */
318 if ((fd = opendisk(dev, O_RDONLY, devpath, sizeof(devpath), 0))
319 == -1)
320 goto next;
321 if (fstat(fd, &sb) == -1)
322 goto close;
323
324 #ifdef DIOCGWEDGEINFO
325 if ((sb.st_mode & S_IFMT) != S_IFREG &&
326 ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) {
327 if (entry > 0)
328 /* wedges and indexes are mutually exclusive */
329 usage_biosboot();
330 dev = dkw.dkw_parent;
331 start = dkw.dkw_offset;
332 size = dkw.dkw_size;
333 }
334 #endif
335 close:
336 close(fd);
337
338 fd = gpt_open(dev);
339 next:
340 if (fd == -1) {
341 warn("unable to open device '%s'", device_name);
342 continue;
343 }
344
345 biosboot(fd);
346
347 gpt_close(fd);
348 }
349
350 return (0);
351 }
352