1 /* $NetBSD: i386.c,v 1.19 2005/11/11 21:09:50 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.19 2005/11/11 21:09:50 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 void 69 show_i386_boot_params(struct x86_boot_params *bpp) 70 { 71 uint32_t i; 72 73 printf("Boot options: "); 74 printf("timeout %d, ", le32toh(bpp->bp_timeout)); 75 printf("flags %x, ", le32toh(bpp->bp_flags)); 76 printf("speed %d, ", le32toh(bpp->bp_conspeed)); 77 printf("ioaddr %x, ", le32toh(bpp->bp_consaddr)); 78 i = le32toh(bpp->bp_consdev); 79 if (i < nelem(console_names) - 1) 80 printf("console %s\n", console_names[i]); 81 else 82 printf("console %d\n", i); 83 if (bpp->bp_keymap[0]) 84 printf(" keymap %s\n", bpp->bp_keymap); 85 } 86 87 static int 88 update_i386_boot_params(ib_params *params, struct x86_boot_params *bpp) 89 { 90 struct x86_boot_params bp; 91 int bplen; 92 int i; 93 94 bplen = le32toh(bpp->bp_length); 95 if (bplen > sizeof bp) 96 /* Ignore pad space in bootxx */ 97 bplen = sizeof bp; 98 99 /* Take (and update) local copy so we handle size mismatches */ 100 memset(&bp, 0, sizeof bp); 101 memcpy(&bp, bpp, bplen); 102 103 if (params->flags & IB_TIMEOUT) 104 bp.bp_timeout = htole32(params->timeout); 105 if (params->flags & IB_RESETVIDEO) 106 bp.bp_flags ^= htole32(X86_BP_FLAGS_RESET_VIDEO); 107 if (params->flags & IB_CONSPEED) 108 bp.bp_conspeed = htole32(params->conspeed); 109 if (params->flags & IB_CONSADDR) 110 bp.bp_consaddr = htole32(params->consaddr); 111 if (params->flags & IB_CONSOLE) { 112 for (i = 0; ; i++) { 113 if (console_names[i] == NULL) { 114 warnx("invalid console name, valid names are:"); 115 fprintf(stderr, "\t%s", console_names[0]); 116 for (i = 1; console_names[i] != NULL; i++) 117 fprintf(stderr, ", %s", console_names[i]); 118 fprintf(stderr, "\n"); 119 return 1; 120 } 121 if (strcmp(console_names[i], params->console) == 0) 122 break; 123 } 124 bp.bp_consdev = htole32(i); 125 } 126 if (params->flags & IB_PASSWORD) { 127 if (params->password[0]) { 128 MD5_CTX md5ctx; 129 MD5Init(&md5ctx); 130 MD5Update(&md5ctx, params->password, 131 strlen(params->password)); 132 MD5Final(bp.bp_password, &md5ctx); 133 bp.bp_flags |= htole32(X86_BP_FLAGS_PASSWORD); 134 } else { 135 memset(&bp.bp_password, 0, sizeof bp.bp_password); 136 bp.bp_flags &= ~htole32(X86_BP_FLAGS_PASSWORD); 137 } 138 } 139 if (params->flags & IB_KEYMAP) 140 strlcpy(bp.bp_keymap, params->keymap, sizeof bp.bp_keymap); 141 142 if (params->flags & (IB_NOWRITE | IB_VERBOSE)) 143 show_i386_boot_params(&bp); 144 145 /* Check we aren't trying to set anything we can't save */ 146 if (bplen < sizeof bp && memcmp((char *)&bp + bplen, 147 (char *)&bp + bplen + 1, 148 sizeof bp - bplen - 1) != 0) { 149 warnx("Patch area in stage1 bootstrap is too small"); 150 return 1; 151 } 152 memcpy(bpp, &bp, bplen); 153 return 0; 154 } 155 156 int 157 i386_setboot(ib_params *params) 158 { 159 int retval, i, bpbsize; 160 uint8_t *bootstrapbuf; 161 u_int bootstrapsize; 162 ssize_t rv; 163 uint32_t magic; 164 struct x86_boot_params *bpp; 165 struct mbr_sector mbr; 166 167 assert(params != NULL); 168 assert(params->fsfd != -1); 169 assert(params->filesystem != NULL); 170 assert(params->s1fd != -1); 171 assert(params->stage1 != NULL); 172 173 retval = 0; 174 bootstrapbuf = NULL; 175 176 /* 177 * There is only 8k of space in a UFSv1 partition (and ustarfs) 178 * so ensure we don't splat over anything important. 179 */ 180 if (params->s1stat.st_size > 8192) { 181 warnx("stage1 bootstrap `%s' is larger than 8192 bytes", 182 params->stage1); 183 goto done; 184 } 185 186 /* 187 * Read in the existing MBR. 188 */ 189 rv = pread(params->fsfd, &mbr, sizeof(mbr), MBR_BBSECTOR); 190 if (rv == -1) { 191 warn("Reading `%s'", params->filesystem); 192 goto done; 193 } else if (rv != sizeof(mbr)) { 194 warnx("Reading `%s': short read", params->filesystem); 195 goto done; 196 } 197 if (mbr.mbr_magic != le16toh(MBR_MAGIC)) { 198 if (params->flags & IB_VERBOSE) { 199 printf( 200 "Ignoring MBR with invalid magic in sector 0 of `%s'\n", 201 params->filesystem); 202 } 203 memset(&mbr, 0, sizeof(mbr)); 204 } 205 206 /* 207 * Allocate a buffer, with space to round up the input file 208 * to the next block size boundary, and with space for the boot 209 * block. 210 */ 211 bootstrapsize = roundup(params->s1stat.st_size, 512); 212 213 bootstrapbuf = malloc(bootstrapsize); 214 if (bootstrapbuf == NULL) { 215 warn("Allocating %u bytes", bootstrapsize); 216 goto done; 217 } 218 memset(bootstrapbuf, 0, bootstrapsize); 219 220 /* 221 * Read the file into the buffer. 222 */ 223 rv = pread(params->s1fd, bootstrapbuf, params->s1stat.st_size, 0); 224 if (rv == -1) { 225 warn("Reading `%s'", params->stage1); 226 goto done; 227 } else if (rv != params->s1stat.st_size) { 228 warnx("Reading `%s': short read", params->stage1); 229 goto done; 230 } 231 232 magic = *(uint32_t *)(bootstrapbuf + 512 * 2 + 4); 233 if (magic != htole32(X86_BOOT_MAGIC_1)) { 234 warnx("Invalid magic in stage1 boostrap %x != %x", 235 magic, htole32(X86_BOOT_MAGIC_1)); 236 goto done; 237 } 238 239 /* 240 * Determine size of BIOS Parameter Block (BPB) to copy from 241 * original MBR to the temporary buffer by examining the first 242 * few instruction in the new bootblock. Supported values: 243 * eb 3c 90 jmp ENDOF(mbr_bpbFAT16)+1, nop 244 * eb 58 90 jmp ENDOF(mbr_bpbFAT32)+1, nop 245 * (anything else) ; don't preserve 246 */ 247 bpbsize = 0; 248 if (bootstrapbuf[0] == 0xeb && bootstrapbuf[2] == 0x90 && 249 (bootstrapbuf[1] == 0x3c || bootstrapbuf[1] == 0x58)) 250 bpbsize = bootstrapbuf[1] + 2 - MBR_BPB_OFFSET; 251 252 /* 253 * Ensure bootxx hasn't got any code or data (i.e, non-zero bytes) in 254 * the partition table. 255 */ 256 for (i = 0; i < sizeof(mbr.mbr_parts); i++) { 257 if (*(uint8_t *)(bootstrapbuf + MBR_PART_OFFSET + i) != 0) { 258 warnx( 259 "Partition table has non-zero byte at offset %d in `%s'", 260 MBR_PART_OFFSET + i, params->stage1); 261 goto done; 262 } 263 } 264 265 /* 266 * Copy the BPB and the partition table from the original MBR to the 267 * temporary buffer so that they're written back to the fs. 268 */ 269 if (bpbsize != 0) { 270 if (params->flags & IB_VERBOSE) 271 printf("Preserving %d (%#x) bytes of the BPB\n", 272 bpbsize, bpbsize); 273 memcpy(bootstrapbuf + MBR_BPB_OFFSET, &mbr.mbr_bpb, bpbsize); 274 } 275 memcpy(bootstrapbuf + MBR_PART_OFFSET, &mbr.mbr_parts, 276 sizeof(mbr.mbr_parts)); 277 278 /* 279 * Fill in any user-specified options into the 280 * struct x86_boot_params 281 * that's 8 bytes in from the start of the third sector. 282 * See sys/arch/i386/stand/bootxx/bootxx.S for more information. 283 */ 284 bpp = (void *)(bootstrapbuf + 512 * 2 + 8); 285 if (update_i386_boot_params(params, bpp)) 286 goto done; 287 288 if (params->flags & IB_NOWRITE) { 289 retval = 1; 290 goto done; 291 } 292 293 /* 294 * Write MBR code to sector zero. 295 */ 296 rv = pwrite(params->fsfd, bootstrapbuf, 512, 0); 297 if (rv == -1) { 298 warn("Writing `%s'", params->filesystem); 299 goto done; 300 } else if (rv != 512) { 301 warnx("Writing `%s': short write", params->filesystem); 302 goto done; 303 } 304 305 /* 306 * Skip disklabel in sector 1 and write bootxx to sectors 2..N. 307 */ 308 rv = pwrite(params->fsfd, bootstrapbuf + 512 * 2, 309 bootstrapsize - 512 * 2, 512 * 2); 310 if (rv == -1) { 311 warn("Writing `%s'", params->filesystem); 312 goto done; 313 } else if (rv != bootstrapsize - 512 * 2) { 314 warnx("Writing `%s': short write", params->filesystem); 315 goto done; 316 } 317 318 retval = 1; 319 320 done: 321 if (bootstrapbuf) 322 free(bootstrapbuf); 323 return retval; 324 } 325 326 int 327 i386_editboot(ib_params *params) 328 { 329 int retval; 330 uint8_t buf[512]; 331 ssize_t rv; 332 uint32_t magic; 333 uint32_t offset; 334 struct x86_boot_params *bpp; 335 336 assert(params != NULL); 337 assert(params->fsfd != -1); 338 assert(params->filesystem != NULL); 339 340 retval = 0; 341 342 /* 343 * Read in the existing bootstrap. 344 */ 345 346 bpp = NULL; 347 for (offset = 0; offset < 4 * 512; offset += 512) { 348 rv = pread(params->fsfd, &buf, sizeof buf, offset); 349 if (rv == -1) { 350 warn("Reading `%s'", params->filesystem); 351 goto done; 352 } else if (rv != sizeof buf) { 353 warnx("Reading `%s': short read", params->filesystem); 354 goto done; 355 } 356 357 magic = *(uint32_t *)(buf + 4) | 0xf; 358 if (magic != htole32(X86_BOOT_MAGIC_1 | 0xf)) 359 continue; 360 bpp = (void *)(buf + 8); 361 break; 362 } 363 if (bpp == NULL) { 364 warnx("Invalid magic in stage1 boostrap"); 365 goto done; 366 } 367 368 /* 369 * Fill in any user-specified options into the 370 * struct x86_boot_params 371 * that's 8 bytes in from the start of the third sector. 372 * See sys/arch/i386/stand/bootxx/bootxx.S for more information. 373 */ 374 if (update_i386_boot_params(params, bpp)) 375 goto done; 376 377 if (params->flags & IB_NOWRITE) { 378 retval = 1; 379 goto done; 380 } 381 382 /* 383 * Write boot code back 384 */ 385 rv = pwrite(params->fsfd, buf, sizeof buf, offset); 386 if (rv == -1) { 387 warn("Writing `%s'", params->filesystem); 388 goto done; 389 } else if (rv != sizeof buf) { 390 warnx("Writing `%s': short write", params->filesystem); 391 goto done; 392 } 393 394 retval = 1; 395 396 done: 397 return retval; 398 } 399