xref: /netbsd-src/usr.sbin/installboot/arch/i386.c (revision f81322cf185a4db50f71fcf7701f20198272620e)
1 /* $NetBSD: i386.c,v 1.22 2006/02/18 10:08:07 dsl Exp $ */
2 
3 /*-
4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by David Laight.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #if HAVE_NBTOOL_CONFIG_H
40 #include "nbtool_config.h"
41 #endif
42 
43 #include <sys/cdefs.h>
44 #if !defined(__lint)
45 __RCSID("$NetBSD: i386.c,v 1.22 2006/02/18 10:08:07 dsl Exp $");
46 #endif /* !__lint */
47 
48 #include <sys/param.h>
49 
50 #include <assert.h>
51 #include <err.h>
52 #include <md5.h>
53 #include <stddef.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #include "installboot.h"
60 
61 #define nelem(x) (sizeof (x)/sizeof *(x))
62 
63 static const char *const console_names[] = {
64 	"pc", "com0", "com1", "com2", "com3",
65 	"com0kbd", "com1kbd", "com2kbd", "com3kbd",
66 	NULL };
67 
68 static int i386_setboot(ib_params *);
69 static int i386_editboot(ib_params *);
70 
71 struct ib_mach ib_mach_i386 =
72 	{ "i386", i386_setboot, no_clearboot, i386_editboot,
73 		IB_RESETVIDEO | IB_CONSOLE | IB_CONSPEED | IB_CONSADDR |
74 		IB_KEYMAP | IB_PASSWORD | IB_TIMEOUT };
75 
76 struct ib_mach ib_mach_amd64 =
77 	{ "amd64", i386_setboot, no_clearboot, i386_editboot,
78 		IB_RESETVIDEO | IB_CONSOLE | IB_CONSPEED | IB_CONSADDR |
79 		IB_KEYMAP | IB_PASSWORD | IB_TIMEOUT };
80 
81 static void
82 show_i386_boot_params(struct x86_boot_params  *bpp)
83 {
84 	uint32_t i;
85 
86 	printf("Boot options:        ");
87 	printf("timeout %d, ", le32toh(bpp->bp_timeout));
88 	printf("flags %x, ", le32toh(bpp->bp_flags));
89 	printf("speed %d, ", le32toh(bpp->bp_conspeed));
90 	printf("ioaddr %x, ", le32toh(bpp->bp_consaddr));
91 	i = le32toh(bpp->bp_consdev);
92 	if (i < nelem(console_names) - 1)
93 		printf("console %s\n", console_names[i]);
94 	else
95 		printf("console %d\n", i);
96 	if (bpp->bp_keymap[0])
97 		printf("                     keymap %s\n", bpp->bp_keymap);
98 }
99 
100 static int
101 update_i386_boot_params(ib_params *params, struct x86_boot_params  *bpp)
102 {
103 	struct x86_boot_params bp;
104 	int bplen;
105 	int i;
106 
107 	bplen = le32toh(bpp->bp_length);
108 	if (bplen > sizeof bp)
109 		/* Ignore pad space in bootxx */
110 		bplen = sizeof bp;
111 
112 	/* Take (and update) local copy so we handle size mismatches */
113 	memset(&bp, 0, sizeof bp);
114 	memcpy(&bp, bpp, bplen);
115 
116 	if (params->flags & IB_TIMEOUT)
117 		bp.bp_timeout = htole32(params->timeout);
118 	if (params->flags & IB_RESETVIDEO)
119 		bp.bp_flags ^= htole32(X86_BP_FLAGS_RESET_VIDEO);
120 	if (params->flags & IB_CONSPEED)
121 		bp.bp_conspeed = htole32(params->conspeed);
122 	if (params->flags & IB_CONSADDR)
123 		bp.bp_consaddr = htole32(params->consaddr);
124 	if (params->flags & IB_CONSOLE) {
125 		for (i = 0; ; i++) {
126 			if (console_names[i] == NULL) {
127 				warnx("invalid console name, valid names are:");
128 				fprintf(stderr, "\t%s", console_names[0]);
129 				for (i = 1; console_names[i] != NULL; i++)
130 					fprintf(stderr, ", %s", console_names[i]);
131 				fprintf(stderr, "\n");
132 				return 1;
133 			}
134 			if (strcmp(console_names[i], params->console) == 0)
135 				break;
136 		}
137 		bp.bp_consdev = htole32(i);
138 	}
139 	if (params->flags & IB_PASSWORD) {
140 		if (params->password[0]) {
141 			MD5_CTX md5ctx;
142 			MD5Init(&md5ctx);
143 			MD5Update(&md5ctx, params->password,
144 			    strlen(params->password));
145 			MD5Final(bp.bp_password, &md5ctx);
146 			bp.bp_flags |= htole32(X86_BP_FLAGS_PASSWORD);
147 		} else {
148 			memset(&bp.bp_password, 0, sizeof bp.bp_password);
149 			bp.bp_flags &= ~htole32(X86_BP_FLAGS_PASSWORD);
150 		}
151 	}
152 	if (params->flags & IB_KEYMAP)
153 		strlcpy(bp.bp_keymap, params->keymap, sizeof bp.bp_keymap);
154 
155 	if (params->flags & (IB_NOWRITE | IB_VERBOSE))
156 		show_i386_boot_params(&bp);
157 
158 	/* Check we aren't trying to set anything we can't save */
159 	if (bplen < sizeof bp && memcmp((char *)&bp + bplen,
160 					(char *)&bp + bplen + 1,
161 					sizeof bp - bplen - 1) != 0) {
162 		warnx("Patch area in stage1 bootstrap is too small");
163 		return 1;
164 	}
165 	memcpy(bpp, &bp, bplen);
166 	return 0;
167 }
168 
169 static int
170 i386_setboot(ib_params *params)
171 {
172 	int		retval, i, bpbsize;
173 	uint8_t		*bootstrapbuf;
174 	u_int		bootstrapsize;
175 	ssize_t		rv;
176 	uint32_t	magic;
177 	struct x86_boot_params	*bpp;
178 	struct mbr_sector	mbr;
179 
180 	assert(params != NULL);
181 	assert(params->fsfd != -1);
182 	assert(params->filesystem != NULL);
183 	assert(params->s1fd != -1);
184 	assert(params->stage1 != NULL);
185 
186 	retval = 0;
187 	bootstrapbuf = NULL;
188 
189 	/*
190 	 * There is only 8k of space in a UFSv1 partition (and ustarfs)
191 	 * so ensure we don't splat over anything important.
192 	 */
193 	if (params->s1stat.st_size > 8192) {
194 		warnx("stage1 bootstrap `%s' is larger than 8192 bytes",
195 			params->stage1);
196 		goto done;
197 	}
198 
199 	/*
200 	 * Read in the existing MBR.
201 	 */
202 	rv = pread(params->fsfd, &mbr, sizeof(mbr), MBR_BBSECTOR);
203 	if (rv == -1) {
204 		warn("Reading `%s'", params->filesystem);
205 		goto done;
206 	} else if (rv != sizeof(mbr)) {
207 		warnx("Reading `%s': short read", params->filesystem);
208 		goto done;
209 	}
210 	if (mbr.mbr_magic != le16toh(MBR_MAGIC)) {
211 		if (params->flags & IB_VERBOSE) {
212 			printf(
213 		    "Ignoring MBR with invalid magic in sector 0 of `%s'\n",
214 			    params->filesystem);
215 		}
216 		memset(&mbr, 0, sizeof(mbr));
217 	}
218 
219 	/*
220 	 * Allocate a buffer, with space to round up the input file
221 	 * to the next block size boundary, and with space for the boot
222 	 * block.
223 	 */
224 	bootstrapsize = roundup(params->s1stat.st_size, 512);
225 
226 	bootstrapbuf = malloc(bootstrapsize);
227 	if (bootstrapbuf == NULL) {
228 		warn("Allocating %u bytes",  bootstrapsize);
229 		goto done;
230 	}
231 	memset(bootstrapbuf, 0, bootstrapsize);
232 
233 	/*
234 	 * Read the file into the buffer.
235 	 */
236 	rv = pread(params->s1fd, bootstrapbuf, params->s1stat.st_size, 0);
237 	if (rv == -1) {
238 		warn("Reading `%s'", params->stage1);
239 		goto done;
240 	} else if (rv != params->s1stat.st_size) {
241 		warnx("Reading `%s': short read", params->stage1);
242 		goto done;
243 	}
244 
245 	magic = *(uint32_t *)(bootstrapbuf + 512 * 2 + 4);
246 	if (magic != htole32(X86_BOOT_MAGIC_1)) {
247 		warnx("Invalid magic in stage1 bootstrap %x != %x",
248 			magic, htole32(X86_BOOT_MAGIC_1));
249 		goto done;
250 	}
251 
252 	/*
253 	 * Determine size of BIOS Parameter Block (BPB) to copy from
254 	 * original MBR to the temporary buffer by examining the first
255 	 * few instruction in the new bootblock.  Supported values:
256 	 *	eb 3c 90	jmp ENDOF(mbr_bpbFAT16)+1, nop
257 	 *	eb 58 90	jmp ENDOF(mbr_bpbFAT32)+1, nop
258 	 *      (anything else)	; don't preserve
259 	 */
260 	bpbsize = 0;
261 	if (bootstrapbuf[0] == 0xeb && bootstrapbuf[2] == 0x90 &&
262 	    (bootstrapbuf[1] == 0x3c || bootstrapbuf[1] == 0x58))
263 		bpbsize = bootstrapbuf[1] + 2 - MBR_BPB_OFFSET;
264 
265 	/*
266 	 * Ensure bootxx hasn't got any code or data (i.e, non-zero bytes) in
267 	 * the partition table.
268 	 */
269 	for (i = 0; i < sizeof(mbr.mbr_parts); i++) {
270 		if (*(uint8_t *)(bootstrapbuf + MBR_PART_OFFSET + i) != 0) {
271 			warnx(
272 		    "Partition table has non-zero byte at offset %d in `%s'",
273 			    MBR_PART_OFFSET + i, params->stage1);
274 			goto done;
275 		}
276 	}
277 
278 	/*
279 	 * Copy the BPB and the partition table from the original MBR to the
280 	 * temporary buffer so that they're written back to the fs.
281 	 */
282 	if (bpbsize != 0) {
283 		if (params->flags & IB_VERBOSE)
284 			printf("Preserving %d (%#x) bytes of the BPB\n",
285 			    bpbsize, bpbsize);
286 		memcpy(bootstrapbuf + MBR_BPB_OFFSET, &mbr.mbr_bpb, bpbsize);
287 	}
288 	memcpy(bootstrapbuf + MBR_PART_OFFSET, &mbr.mbr_parts,
289 	    sizeof(mbr.mbr_parts));
290 
291 	/*
292 	 * Fill in any user-specified options into the
293 	 *      struct x86_boot_params
294 	 * that's 8 bytes in from the start of the third sector.
295 	 * See sys/arch/i386/stand/bootxx/bootxx.S for more information.
296 	 */
297 	bpp = (void *)(bootstrapbuf + 512 * 2 + 8);
298 	if (update_i386_boot_params(params, bpp))
299 		goto done;
300 
301 	if (params->flags & IB_NOWRITE) {
302 		retval = 1;
303 		goto done;
304 	}
305 
306 	/*
307 	 * Write MBR code to sector zero.
308 	 */
309 	rv = pwrite(params->fsfd, bootstrapbuf, 512, 0);
310 	if (rv == -1) {
311 		warn("Writing `%s'", params->filesystem);
312 		goto done;
313 	} else if (rv != 512) {
314 		warnx("Writing `%s': short write", params->filesystem);
315 		goto done;
316 	}
317 
318 	/*
319 	 * Skip disklabel in sector 1 and write bootxx to sectors 2..N.
320 	 */
321 	rv = pwrite(params->fsfd, bootstrapbuf + 512 * 2,
322 		    bootstrapsize - 512 * 2, 512 * 2);
323 	if (rv == -1) {
324 		warn("Writing `%s'", params->filesystem);
325 		goto done;
326 	} else if (rv != bootstrapsize - 512 * 2) {
327 		warnx("Writing `%s': short write", params->filesystem);
328 		goto done;
329 	}
330 
331 	retval = 1;
332 
333  done:
334 	if (bootstrapbuf)
335 		free(bootstrapbuf);
336 	return retval;
337 }
338 
339 static int
340 i386_editboot(ib_params *params)
341 {
342 	int		retval;
343 	uint8_t		buf[512];
344 	ssize_t		rv;
345 	uint32_t	magic;
346 	uint32_t	offset;
347 	struct x86_boot_params	*bpp;
348 
349 	assert(params != NULL);
350 	assert(params->fsfd != -1);
351 	assert(params->filesystem != NULL);
352 
353 	retval = 0;
354 
355 	/*
356 	 * Read in the existing bootstrap.
357 	 * Look in any of the first 4 sectors.
358 	 */
359 
360 	bpp = NULL;
361 	for (offset = 0; offset < 4 * 512; offset += 512) {
362 		rv = pread(params->fsfd, &buf, sizeof buf, offset);
363 		if (rv == -1) {
364 			warn("Reading `%s'", params->filesystem);
365 			goto done;
366 		} else if (rv != sizeof buf) {
367 			warnx("Reading `%s': short read", params->filesystem);
368 			goto done;
369 		}
370 
371 		/* Magic number is 4 bytes in (to allow for a jmps) */
372 		/* Also allow any of the magic numbers. */
373 		magic = le32toh(*(uint32_t *)(buf + 4)) | 0xf;
374 		if (magic != (X86_BOOT_MAGIC_1 | 0xf))
375 			continue;
376 
377 		/* The parameters are just after the magic number */
378 		bpp = (void *)(buf + 8);
379 		break;
380 	}
381 	if (bpp == NULL) {
382 		warnx("Invalid magic in existing bootstrap");
383 		goto done;
384 	}
385 
386 	/*
387 	 * Fill in any user-specified options into the
388 	 *      struct x86_boot_params
389 	 * that's 8 bytes in from the start of the third sector.
390 	 * See sys/arch/i386/stand/bootxx/bootxx.S for more information.
391 	 */
392 	if (update_i386_boot_params(params, bpp))
393 		goto done;
394 
395 	if (params->flags & IB_NOWRITE) {
396 		retval = 1;
397 		goto done;
398 	}
399 
400 	/*
401 	 * Write boot code back
402 	 */
403 	rv = pwrite(params->fsfd, buf, sizeof buf, offset);
404 	if (rv == -1) {
405 		warn("Writing `%s'", params->filesystem);
406 		goto done;
407 	} else if (rv != sizeof buf) {
408 		warnx("Writing `%s': short write", params->filesystem);
409 		goto done;
410 	}
411 
412 	retval = 1;
413 
414  done:
415 	return retval;
416 }
417