Home | History | Annotate | Line # | Download | only in arch
i386.c revision 1.23
      1 /* $NetBSD: i386.c,v 1.23 2007/01/06 10:21:24 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.23 2007/01/06 10:21:24 dsl Exp $");
     46 #endif /* !__lint */
     47 
     48 #include <sys/param.h>
     49 #ifndef HAVE_NBTOOL_CONFIG_H
     50 #include <sys/ioctl.h>
     51 #include <sys/dkio.h>
     52 #endif
     53 
     54 #include <assert.h>
     55 #include <errno.h>
     56 #include <err.h>
     57 #include <md5.h>
     58 #include <stddef.h>
     59 #include <stdio.h>
     60 #include <stdlib.h>
     61 #include <string.h>
     62 #include <unistd.h>
     63 
     64 #include "installboot.h"
     65 
     66 #define nelem(x) (sizeof (x)/sizeof *(x))
     67 
     68 static const char *const console_names[] = {
     69 	"pc", "com0", "com1", "com2", "com3",
     70 	"com0kbd", "com1kbd", "com2kbd", "com3kbd",
     71 	NULL };
     72 
     73 static int i386_setboot(ib_params *);
     74 static int i386_editboot(ib_params *);
     75 
     76 struct ib_mach ib_mach_i386 =
     77 	{ "i386", i386_setboot, no_clearboot, i386_editboot,
     78 		IB_RESETVIDEO | IB_CONSOLE | IB_CONSPEED | IB_CONSADDR |
     79 		IB_KEYMAP | IB_PASSWORD | IB_TIMEOUT };
     80 
     81 struct ib_mach ib_mach_amd64 =
     82 	{ "amd64", i386_setboot, no_clearboot, i386_editboot,
     83 		IB_RESETVIDEO | IB_CONSOLE | IB_CONSPEED | IB_CONSADDR |
     84 		IB_KEYMAP | IB_PASSWORD | IB_TIMEOUT };
     85 
     86 /*
     87  * Attempting to write the 'labelsector' (or a sector near it - within 8k?)
     88  * using the non-raw disk device fails silently.  This can be detected (today)
     89  * by doing a fsync() and a read back.
     90  * This is very likely to affect installboot, indeed the code may need to
     91  * be written into the 'labelsector' itself - especially on non-512 byte media.
     92  * We do all writes with a read verify.
     93  * If EROFS is returned we also try to enable writes to the label sector.
     94  * (Maybe these functions should be in the generic part of installboot.)
     95  */
     96 static int
     97 pwrite_validate(int fd, const void *buf, size_t n_bytes, off_t offset)
     98 {
     99 	void *r_buf;
    100 	ssize_t rv;
    101 
    102 	r_buf = malloc(n_bytes);
    103 	if (r_buf == NULL)
    104 		return -1;
    105 	rv = pwrite(fd, buf, n_bytes, offset);
    106 	if (rv == -1) {
    107 		free(r_buf);
    108 		return -1;
    109 	}
    110 	fsync(fd);
    111 	if (pread(fd, r_buf, rv, offset) == rv && memcmp(r_buf, buf, rv) == 0)
    112 		return rv;
    113 	errno = EROFS;
    114 	return -1;
    115 }
    116 
    117 static int
    118 write_boot_area(ib_params *params, void *v_buf, int len)
    119 {
    120 	int rv, i;
    121 	uint8_t *buf = v_buf;
    122 
    123 	/*
    124 	 * Writing the 'label' sector (likely to be bytes 512-1023) could
    125 	 * fail, so we try to avoid writing that area.
    126 	 * Unfortunately, if we are accessing the raw disk, and the sector
    127 	 * size is larger than 512 bytes that is also doomed.
    128 	 * See how we get on....
    129 	 *
    130 	 * NB: Even if the physical sector size is not 512, the space for
    131 	 * the label is 512 bytes from the start of the disk.
    132 	 * So all the '512' constants in these functions are correct.
    133 	 */
    134 
    135 	/* Write out first 512 bytes - the pbr code */
    136 	rv = pwrite_validate(params->fsfd, buf, 512, 0);
    137 	if (rv == 512) {
    138 		/* That worked, do the rest */
    139 		if (len == 512)
    140 			return 1;
    141 		len -= 512 * 2;
    142 		rv = pwrite_validate(params->fsfd, buf + 512 * 2, len, 512 * 2);
    143 		if (rv != len)
    144 			goto bad_write;
    145 		return 1;
    146 	}
    147 	if (rv != -1 || (errno != EINVAL && errno != EROFS))
    148 		goto bad_write;
    149 
    150 	if (errno == EINVAL) {
    151 		/* Assume the failure was due to to the sector size > 512 */
    152 		rv = pwrite_validate(params->fsfd, buf, len, 0);
    153 		if (rv == len)
    154 			return 1;
    155 		if (rv != -1 || (errno != EROFS))
    156 			goto bad_write;
    157 	}
    158 
    159 #ifdef DIOCWLABEL
    160 	/* Pesky label is protected, try to unprotect it */
    161 	i = 1;
    162 	rv = ioctl(params->fsfd, DIOCWLABEL, &i);
    163 	if (rv != 0) {
    164 		warn("Cannot enable writes to the label sector");
    165 		return 0;
    166 	}
    167 	/* Try again with label write-enabled */
    168 	rv = pwrite_validate(params->fsfd, buf, len, 0);
    169 
    170 	/* Reset write-protext */
    171 	i = 0;
    172 	ioctl(params->fsfd, DIOCWLABEL, &i);
    173 	if (rv == len)
    174 		return 1;
    175 #endif
    176 
    177   bad_write:
    178 	if (rv == -1)
    179 		warn("Writing `%s'", params->filesystem);
    180 	else
    181 		warnx("Writing `%s': short write, %u bytes",
    182 			params->filesystem, rv);
    183 	return 0;
    184 }
    185 
    186 static void
    187 show_i386_boot_params(struct x86_boot_params  *bpp)
    188 {
    189 	uint32_t i;
    190 
    191 	printf("Boot options:        ");
    192 	printf("timeout %d, ", le32toh(bpp->bp_timeout));
    193 	printf("flags %x, ", le32toh(bpp->bp_flags));
    194 	printf("speed %d, ", le32toh(bpp->bp_conspeed));
    195 	printf("ioaddr %x, ", le32toh(bpp->bp_consaddr));
    196 	i = le32toh(bpp->bp_consdev);
    197 	if (i < nelem(console_names) - 1)
    198 		printf("console %s\n", console_names[i]);
    199 	else
    200 		printf("console %d\n", i);
    201 	if (bpp->bp_keymap[0])
    202 		printf("                     keymap %s\n", bpp->bp_keymap);
    203 }
    204 
    205 static int
    206 is_zero(const uint8_t *p, unsigned int len)
    207 {
    208 	return len == 0 || (p[0] == 0 && memcmp(p, p + 1, len - 1) == 0);
    209 }
    210 
    211 static int
    212 update_i386_boot_params(ib_params *params, struct x86_boot_params  *bpp)
    213 {
    214 	struct x86_boot_params bp;
    215 	int bplen;
    216 	int i;
    217 
    218 	bplen = le32toh(bpp->bp_length);
    219 	if (bplen > sizeof bp)
    220 		/* Ignore pad space in bootxx */
    221 		bplen = sizeof bp;
    222 
    223 	/* Take (and update) local copy so we handle size mismatches */
    224 	memset(&bp, 0, sizeof bp);
    225 	memcpy(&bp, bpp, bplen);
    226 
    227 	if (params->flags & IB_TIMEOUT)
    228 		bp.bp_timeout = htole32(params->timeout);
    229 	if (params->flags & IB_RESETVIDEO)
    230 		bp.bp_flags ^= htole32(X86_BP_FLAGS_RESET_VIDEO);
    231 	if (params->flags & IB_CONSPEED)
    232 		bp.bp_conspeed = htole32(params->conspeed);
    233 	if (params->flags & IB_CONSADDR)
    234 		bp.bp_consaddr = htole32(params->consaddr);
    235 	if (params->flags & IB_CONSOLE) {
    236 		for (i = 0; ; i++) {
    237 			if (console_names[i] == NULL) {
    238 				warnx("invalid console name, valid names are:");
    239 				fprintf(stderr, "\t%s", console_names[0]);
    240 				for (i = 1; console_names[i] != NULL; i++)
    241 					fprintf(stderr, ", %s", console_names[i]);
    242 				fprintf(stderr, "\n");
    243 				return 1;
    244 			}
    245 			if (strcmp(console_names[i], params->console) == 0)
    246 				break;
    247 		}
    248 		bp.bp_consdev = htole32(i);
    249 	}
    250 	if (params->flags & IB_PASSWORD) {
    251 		if (params->password[0]) {
    252 			MD5_CTX md5ctx;
    253 			MD5Init(&md5ctx);
    254 			MD5Update(&md5ctx, params->password,
    255 			    strlen(params->password));
    256 			MD5Final(bp.bp_password, &md5ctx);
    257 			bp.bp_flags |= htole32(X86_BP_FLAGS_PASSWORD);
    258 		} else {
    259 			memset(&bp.bp_password, 0, sizeof bp.bp_password);
    260 			bp.bp_flags &= ~htole32(X86_BP_FLAGS_PASSWORD);
    261 		}
    262 	}
    263 	if (params->flags & IB_KEYMAP)
    264 		strlcpy(bp.bp_keymap, params->keymap, sizeof bp.bp_keymap);
    265 
    266 	if (params->flags & (IB_NOWRITE | IB_VERBOSE))
    267 		show_i386_boot_params(&bp);
    268 
    269 	/* Check we aren't trying to set anything we can't save */
    270 	if (!is_zero((char *)&bp + bplen, sizeof bp - bplen)) {
    271 		warnx("Patch area in stage1 bootstrap is too small");
    272 		return 1;
    273 	}
    274 	memcpy(bpp, &bp, bplen);
    275 	return 0;
    276 }
    277 
    278 static int
    279 i386_setboot(ib_params *params)
    280 {
    281 	unsigned int	u;
    282 	ssize_t		rv;
    283 	uint32_t	*magic, expected_magic;
    284 	union {
    285 	    struct mbr_sector	mbr;
    286 	    uint8_t		b[8192];
    287 	} disk_buf, bootstrap;
    288 
    289 	assert(params != NULL);
    290 	assert(params->fsfd != -1);
    291 	assert(params->filesystem != NULL);
    292 	assert(params->s1fd != -1);
    293 	assert(params->stage1 != NULL);
    294 
    295 	/*
    296 	 * There is only 8k of space in a UFSv1 partition (and ustarfs)
    297 	 * so ensure we don't splat over anything important.
    298 	 */
    299 	if (params->s1stat.st_size > sizeof bootstrap) {
    300 		warnx("stage1 bootstrap `%s' (%u bytes) is larger than 8192 bytes",
    301 			params->stage1, (unsigned int)params->s1stat.st_size);
    302 		return 0;
    303 	}
    304 	if (params->s1stat.st_size < 3 * 512 && params->s1stat.st_size != 512) {
    305 		warnx("stage1 bootstrap `%s' (%u bytes) is too small",
    306 			params->stage1, (unsigned int)params->s1stat.st_size);
    307 		return 0;
    308 	}
    309 
    310 	/* Read in the existing disk header and boot code */
    311 	rv = pread(params->fsfd, &disk_buf, sizeof (disk_buf), 0);
    312 	if (rv != sizeof (disk_buf)) {
    313 		if (rv == -1)
    314 			warn("Reading `%s'", params->filesystem);
    315 		else
    316 			warnx("Reading `%s': short read, %d bytes",
    317 				    params->filesystem, rv);
    318 		return 0;
    319 	}
    320 
    321 	if (disk_buf.mbr.mbr_magic != le16toh(MBR_MAGIC)) {
    322 		if (params->flags & IB_VERBOSE) {
    323 			printf(
    324 		    "Ignoring PBR with invalid magic in sector 0 of `%s'\n",
    325 			    params->filesystem);
    326 		}
    327 		memset(&disk_buf, 0, 512);
    328 	}
    329 
    330 	/* Read the new bootstrap code. */
    331 	rv = pread(params->s1fd, &bootstrap, params->s1stat.st_size, 0);
    332 	if (rv != params->s1stat.st_size) {
    333 		if (rv == -1)
    334 			warn("Reading `%s'", params->stage1);
    335 		else
    336 			warnx("Reading `%s': short read, %d bytes",
    337 				params->stage1, rv);
    338 		return 0;
    339 	}
    340 
    341 	/*
    342 	 * The bootstrap code is either 512 bytes for booting FAT16, or best
    343 	 * part of 8k (with bytes 512-1023 all zeros).
    344 	 */
    345 	if (params->s1stat.st_size == 512) {
    346 		/* Magic number is at end of pbr code */
    347 		magic = (void *)(bootstrap.b + 512 - 16 + 4);
    348 		expected_magic = htole32(X86_BOOT_MAGIC_FAT);
    349 	} else {
    350 		/* Magic number is at start of sector following label */
    351 		magic = (void *)(bootstrap.b + 512 * 2 + 4);
    352 		expected_magic = htole32(X86_BOOT_MAGIC_1);
    353 		/*
    354 		 * For a variety of reasons we restrict our 'normal' partition
    355 		 * boot code to a size which enable it to be used as mbr code.
    356 		 * IMHO this is bugus (dsl).
    357 		 */
    358 		if (!is_zero(bootstrap.b + 512-2-64, 64)) {
    359 			warnx("Data in mbr partition table of new bootstrap");
    360 			return 0;
    361 		}
    362 		if (!is_zero(bootstrap.b + 512, 512)) {
    363 			warnx("Data in label part of new bootstrap");
    364 			return 0;
    365 		}
    366 		/* Copy mbr table and label from existing disk buffer */
    367 		memcpy(bootstrap.b + 512-2-64, disk_buf.b + 512-2-64, 64);
    368 		memcpy(bootstrap.b + 512, disk_buf.b + 512, 512);
    369 	}
    370 
    371 	/* Validate the 'magic number' that marks the parameter block */
    372 	if (*magic != expected_magic) {
    373 		warnx("Invalid magic in stage1 bootstrap %x != %x",
    374 				*magic, expected_magic);
    375 		return 0;
    376 	}
    377 
    378 	/*
    379 	 * For FAT compatibility, the pbr code starts 'jmp xx; nop' followed
    380 	 * by the BIOS Parameter Block (BPB).
    381 	 * The 2nd byte (jump offset) is the size of the nop + BPB.
    382 	 */
    383 	if (bootstrap.b[0] != 0xeb || bootstrap.b[2] != 0x90) {
    384 		warnx("No BPB in new bootstrap %02x:%02x:%02x",
    385 			bootstrap.b[0], bootstrap.b[1], bootstrap.b[2]);
    386 		return 0;
    387 	}
    388 
    389 	/* Find size of old BPB, and copy into new bootcode */
    390 	if (!is_zero(disk_buf.b + 3 + 8, disk_buf.b[1] - 1 - 8)) {
    391 		struct mbr_bpbFAT16 *bpb = (void *)(disk_buf.b + 3 + 8);
    392 		/* Check enough space before first FAT for the bootcode */
    393 		u = le16toh(bpb->bpbBytesPerSec) * le16toh(bpb->bpbResSectors);
    394 		if (u != 0 && u < params->s1stat.st_size) {
    395 			warnx("Insufficient reserved space (%u bytes)", u);
    396 			return 0;
    397 		}
    398 		/* Check we have enough space for the old bpb */
    399 		if (disk_buf.b[1] > bootstrap.b[1]) {
    400 			/* old BPB is larger, allow if extra zeros */
    401 			if (!is_zero(disk_buf.b + 2 + bootstrap.b[1],
    402 			    disk_buf.b[1] - bootstrap.b[1])) {
    403 				warnx("Old BPB too big");
    404 				    return 0;
    405 			}
    406 			u = bootstrap.b[1];
    407 		} else {
    408 			/* Old BPB is shorter, leave zero filled */
    409 			u = disk_buf.b[1];
    410 		}
    411 		memcpy(bootstrap.b + 2, disk_buf.b + 2, u);
    412 	}
    413 
    414 	/*
    415 	 * Fill in any user-specified options into the
    416 	 *      struct x86_boot_params
    417 	 * that follows the magic number.
    418 	 * See sys/arch/i386/stand/bootxx/bootxx.S for more information.
    419 	 */
    420 	if (update_i386_boot_params(params, (void *)(magic + 1)))
    421 		return 0;
    422 
    423 	if (params->flags & IB_NOWRITE) {
    424 		return 1;
    425 	}
    426 
    427 	/* Copy new bootstrap data into disk buffer, ignoring label area */
    428 	memcpy(&disk_buf, &bootstrap, 512);
    429 	if (params->s1stat.st_size > 512 * 2) {
    430 		memcpy(disk_buf.b + 2 * 512, bootstrap.b + 2 * 512,
    431 		    params->s1stat.st_size - 2 * 512);
    432 		/* Zero pad to 512 byte sector boundary */
    433 		memset(disk_buf.b + params->s1stat.st_size, 0,
    434 			(8192 - params->s1stat.st_size) & 511);
    435 	}
    436 
    437 	return write_boot_area(params, &disk_buf, sizeof disk_buf);
    438 }
    439 
    440 static int
    441 i386_editboot(ib_params *params)
    442 {
    443 	int		retval;
    444 	uint8_t		buf[512];
    445 	ssize_t		rv;
    446 	uint32_t	magic;
    447 	uint32_t	offset;
    448 	struct x86_boot_params	*bpp;
    449 
    450 	assert(params != NULL);
    451 	assert(params->fsfd != -1);
    452 	assert(params->filesystem != NULL);
    453 
    454 	retval = 0;
    455 
    456 	/*
    457 	 * Read in the existing bootstrap.
    458 	 * Look in any of the first 4 sectors.
    459 	 */
    460 
    461 	bpp = NULL;
    462 	for (offset = 0; offset < 4 * 512; offset += 512) {
    463 		rv = pread(params->fsfd, &buf, sizeof buf, offset);
    464 		if (rv == -1) {
    465 			warn("Reading `%s'", params->filesystem);
    466 			goto done;
    467 		} else if (rv != sizeof buf) {
    468 			warnx("Reading `%s': short read", params->filesystem);
    469 			goto done;
    470 		}
    471 
    472 		/* Magic number is 4 bytes in (to allow for a jmps) */
    473 		/* Also allow any of the magic numbers. */
    474 		magic = le32toh(*(uint32_t *)(buf + 4)) | 0xf;
    475 		if (magic != (X86_BOOT_MAGIC_1 | 0xf))
    476 			continue;
    477 
    478 		/* The parameters are just after the magic number */
    479 		bpp = (void *)(buf + 8);
    480 		break;
    481 	}
    482 	if (bpp == NULL) {
    483 		warnx("Invalid magic in existing bootstrap");
    484 		goto done;
    485 	}
    486 
    487 	/*
    488 	 * Fill in any user-specified options into the
    489 	 *      struct x86_boot_params
    490 	 * that's 8 bytes in from the start of the third sector.
    491 	 * See sys/arch/i386/stand/bootxx/bootxx.S for more information.
    492 	 */
    493 	if (update_i386_boot_params(params, bpp))
    494 		goto done;
    495 
    496 	if (params->flags & IB_NOWRITE) {
    497 		retval = 1;
    498 		goto done;
    499 	}
    500 
    501 	/*
    502 	 * Write boot code back
    503 	 */
    504 	rv = pwrite(params->fsfd, buf, sizeof buf, offset);
    505 	if (rv == -1) {
    506 		warn("Writing `%s'", params->filesystem);
    507 		goto done;
    508 	} else if (rv != sizeof buf) {
    509 		warnx("Writing `%s': short write", params->filesystem);
    510 		goto done;
    511 	}
    512 
    513 	retval = 1;
    514 
    515  done:
    516 	return retval;
    517 }
    518