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