1 /* $NetBSD: md.c,v 1.14 2018/11/27 17:13:41 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Based on code written by Philip A. Nelson for Piermont Information 8 * Systems Inc. 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. The name of Piermont Information Systems Inc. may not be used to endorse 19 * or promote products derived from this software without specific prior 20 * written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* md.c -- i386 machine specific routines - also used by amd64 */ 36 37 #include <sys/param.h> 38 #include <sys/sysctl.h> 39 #include <sys/exec.h> 40 #include <sys/utsname.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <machine/cpu.h> 44 #include <stdio.h> 45 #include <stddef.h> 46 #include <util.h> 47 #include <dirent.h> 48 #include <termios.h> 49 50 #include "defs.h" 51 #include "md.h" 52 #include "endian.h" 53 #include "msg_defs.h" 54 #include "menu_defs.h" 55 56 #ifdef NO_LBA_READS /* for testing */ 57 #undef BIFLAG_EXTINT13 58 #define BIFLAG_EXTINT13 0 59 #endif 60 61 static struct biosdisk_info *biosdisk = NULL; 62 63 /* prototypes */ 64 65 static int get_bios_info(char *); 66 static int mbr_root_above_chs(void); 67 static void md_upgrade_mbrtype(void); 68 static int md_read_bootcode(const char *, struct mbr_sector *); 69 static unsigned int get_bootmodel(void); 70 71 static int conmib[] = {CTL_MACHDEP, CPU_CONSDEV}; 72 73 void 74 md_init(void) 75 { 76 } 77 78 void 79 md_init_set_status(int flags) 80 { 81 (void)flags; 82 83 /* Default to install same type of kernel as we are running */ 84 set_kernel_set(get_bootmodel()); 85 } 86 87 int 88 md_get_info(void) 89 { 90 mbr_info_t *ext; 91 struct mbr_partition *p; 92 const char *bootcode; 93 int i; 94 int names, fl, ofl; 95 #define ACTIVE_FOUND 0x0100 96 #define NETBSD_ACTIVE 0x0200 97 #define NETBSD_NAMED 0x0400 98 #define ACTIVE_NAMED 0x0800 99 100 if (pm->no_mbr) 101 return 1; 102 103 if (read_mbr(pm->diskdev, &mbr) < 0) 104 memset(&mbr.mbr, 0, sizeof mbr.mbr - 2); 105 get_bios_info(pm->diskdev); 106 107 edit: 108 if (edit_mbr(&mbr) == 0) 109 return 0; 110 111 root_limit = 0; 112 if (biosdisk != NULL && (biosdisk->bi_flags & BIFLAG_EXTINT13) == 0) { 113 if (mbr_root_above_chs()) { 114 msg_display(MSG_partabovechs); 115 if (!ask_noyes(NULL)) 116 goto edit; 117 /* The user is shooting themselves in the foot here...*/ 118 } else 119 root_limit = bcyl * bhead * bsec; 120 } 121 122 /* 123 * Ensure the install partition (at sector pm->ptstart) and the active 124 * partition are bootable. 125 * Determine whether the bootselect code is needed. 126 * Note that MBR_BS_NEWMBR is always set, so we ignore it! 127 */ 128 fl = 0; 129 names = 0; 130 for (ext = &mbr; ext != NULL; ext = ext->extended) { 131 p = ext->mbr.mbr_parts; 132 for (i = 0; i < MBR_PART_COUNT; p++, i++) { 133 if (p->mbrp_flag == MBR_PFLAG_ACTIVE) { 134 fl |= ACTIVE_FOUND; 135 if (ext->sector + p->mbrp_start == pm->ptstart) 136 fl |= NETBSD_ACTIVE; 137 } 138 if (ext->mbrb.mbrbs_nametab[i][0] == 0) { 139 /* No bootmenu label... */ 140 if (ext->sector == 0) 141 continue; 142 if (ext->sector + p->mbrp_start == pm->ptstart) 143 /* 144 * Have installed into an extended ptn 145 * force name & bootsel... 146 */ 147 names++; 148 continue; 149 } 150 /* Partition has a bootmenu label... */ 151 if (ext->sector != 0) 152 fl |= MBR_BS_EXTLBA; 153 if (ext->sector + p->mbrp_start == pm->ptstart) 154 fl |= NETBSD_NAMED; 155 else if (p->mbrp_flag == MBR_PFLAG_ACTIVE) 156 fl |= ACTIVE_NAMED; 157 else 158 names++; 159 } 160 } 161 if (!(fl & ACTIVE_FOUND)) 162 fl |= NETBSD_ACTIVE; 163 if (fl & NETBSD_NAMED && fl & NETBSD_ACTIVE) 164 fl |= ACTIVE_NAMED; 165 166 if ((names > 0 || !(fl & NETBSD_ACTIVE)) && 167 (!(fl & NETBSD_NAMED) || !(fl & ACTIVE_NAMED))) { 168 /* 169 * There appear to be multiple bootable partitions, but they 170 * don't all have bootmenu texts. 171 */ 172 msg_display(MSG_missing_bootmenu_text); 173 if (ask_yesno(NULL)) 174 goto edit; 175 } 176 177 if ((fl & MBR_BS_EXTLBA) && 178 (biosdisk == NULL || !(biosdisk->bi_flags & BIFLAG_EXTINT13))) { 179 /* Need unsupported LBA reads to read boot sectors */ 180 msg_display(MSG_no_extended_bootmenu); 181 if (!ask_noyes(NULL)) 182 goto edit; 183 } 184 185 /* Sort out the name of the mbr code we need */ 186 if (names > 0 || fl & (NETBSD_NAMED | ACTIVE_NAMED)) { 187 /* Need bootselect code */ 188 fl |= MBR_BS_ACTIVE; 189 bootcode = fl & MBR_BS_EXTLBA ? _PATH_BOOTEXT : _PATH_BOOTSEL; 190 } else 191 bootcode = _PATH_MBR; 192 193 fl &= MBR_BS_ACTIVE | MBR_BS_EXTLBA; 194 195 /* Look at what is installed */ 196 ofl = mbr.mbrb.mbrbs_flags; 197 if (ofl == 0) { 198 /* Check there is some bootcode at all... */ 199 if (mbr.mbr.mbr_magic != htole16(MBR_MAGIC) || 200 mbr.mbr.mbr_jmpboot[0] == 0 || 201 mbr_root_above_chs()) 202 /* Existing won't do, force update */ 203 fl |= MBR_BS_NEWMBR; 204 } 205 ofl = mbr.oflags & (MBR_BS_ACTIVE | MBR_BS_EXTLBA); 206 207 if (fl & ~ofl || (fl == 0 && ofl & MBR_BS_ACTIVE)) { 208 /* Existing boot code isn't the right one... */ 209 if (fl & MBR_BS_ACTIVE) 210 msg_display(MSG_installbootsel); 211 else 212 msg_display(MSG_installmbr); 213 } else 214 /* Existing code would (probably) be ok */ 215 msg_display(MSG_updatembr); 216 217 if (!ask_yesno(NULL)) 218 /* User doesn't want to update mbr code */ 219 return 1; 220 221 if (md_read_bootcode(bootcode, &mbr.mbr) == 0) 222 /* update suceeded - to memory copy */ 223 return 1; 224 225 /* This shouldn't happen since the files are in the floppy fs... */ 226 msg_display("Can't find %s", bootcode); 227 ask_yesno(NULL); 228 229 return 1; 230 } 231 232 /* 233 * md back-end code for menu-driven BSD disklabel editor. 234 */ 235 int 236 md_make_bsd_partitions(void) 237 { 238 return make_bsd_partitions(); 239 } 240 241 /* 242 * any additional partition validation 243 */ 244 int 245 md_check_partitions(void) 246 { 247 int rval; 248 char *bootxx; 249 250 /* check we have boot code for the root partition type */ 251 bootxx = bootxx_name(); 252 rval = access(bootxx, R_OK); 253 free(bootxx); 254 if (rval == 0) 255 return 1; 256 process_menu(MENU_ok, __UNCONST(MSG_No_Bootcode)); 257 return 0; 258 } 259 260 /* 261 * hook called before writing new disklabel. 262 */ 263 int 264 md_pre_disklabel(void) 265 { 266 if (pm->no_mbr) 267 return 0; 268 269 msg_display(MSG_dofdisk); 270 271 /* write edited MBR onto disk. */ 272 if (write_mbr(pm->diskdev, &mbr, 1) != 0) { 273 msg_display(MSG_wmbrfail); 274 process_menu(MENU_ok, NULL); 275 return 1; 276 } 277 return 0; 278 } 279 280 /* 281 * hook called after writing disklabel to new target disk. 282 */ 283 int 284 md_post_disklabel(void) 285 { 286 return 0; 287 } 288 289 /* 290 * hook called after upgrade() or install() has finished setting 291 * up the target disk but immediately before the user is given the 292 * ``disks are now set up'' message. 293 */ 294 int 295 md_post_newfs(void) 296 { 297 int ret; 298 size_t len; 299 char boot_options[1024]; 300 char *bootxx_filename; 301 /* 302 * XXX - this code retains a lot of cruft from when we went 303 * to great pains to exclude installboot from the ramdisk 304 * for size reasons and should be rewritten. 305 */ 306 static const char *consoles[]={ 307 "pc", /* CONSDEV_PC */ 308 "com0", /* CONSDEV_COM0 */ 309 "com1", /* CONSDEV_COM1 */ 310 "com2", /* CONSDEV_COM2 */ 311 "com3", /* CONSDEV_COM3 */ 312 "com0kbd", /* CONSDEV_COM0KBD */ 313 "com1kbd", /* CONSDEV_COM1KBD */ 314 "com2kbd", /* CONSDEV_COM2KBD */ 315 "com3kbd" /* CONSDEV_COM3KBD */ }; 316 static struct x86_boot_params boottype = 317 {sizeof boottype, 0, 5, 0, 9600, { '\0' }, "", 0}; 318 struct termios t; 319 dev_t condev; 320 321 if (pm == NULL || !pm->no_part) { 322 /* 323 * Get console device, should either be ttyE0 or tty0n. 324 * Too hard to double check, so just 'know' the device numbers. 325 */ 326 len = sizeof condev; 327 if (sysctl(conmib, nelem(conmib), &condev, &len, NULL, 0) != -1 328 && (condev & ~3) == 0x800) { 329 /* Motherboard serial port */ 330 boottype.bp_consdev = (condev & 3) + 1; 331 /* Defaulting the baud rate to that of stdin should suffice */ 332 if (tcgetattr(0, &t) != -1) 333 boottype.bp_conspeed = t.c_ispeed; 334 } 335 336 process_menu(MENU_getboottype, &boottype); 337 msg_display(MSG_dobootblks, pm->diskdev); 338 if (boottype.bp_consdev == ~0u) 339 /* Use existing bootblocks */ 340 return 0; 341 } 342 343 ret = cp_to_target("/usr/mdec/boot", "/boot"); 344 if (ret) 345 return ret; 346 if (pm && pm->no_part) 347 return 0; 348 349 bootxx_filename = bootxx_name(); 350 if (bootxx_filename != NULL) { 351 snprintf(boot_options, sizeof boot_options, 352 "console=%s,speed=%u", consoles[boottype.bp_consdev], 353 boottype.bp_conspeed); 354 if (pm->isspecial) { 355 ret = run_program(RUN_DISPLAY, 356 "/usr/sbin/installboot -o %s /dev/r%s %s", 357 boot_options, pm->diskdev, bootxx_filename); 358 } else { 359 ret = run_program(RUN_DISPLAY, 360 "/usr/sbin/installboot -o %s /dev/r%s%c %s", 361 boot_options, pm->diskdev, 'a' + pm->rootpart, 362 bootxx_filename); 363 } 364 free(bootxx_filename); 365 } else 366 ret = -1; 367 368 if (ret != 0) 369 process_menu(MENU_ok, 370 __UNCONST("Warning: disk is probably not bootable")); 371 372 return ret; 373 } 374 375 int 376 md_post_extract(void) 377 { 378 return 0; 379 } 380 381 void 382 md_cleanup_install(void) 383 { 384 size_t len; 385 dev_t condev; 386 387 #ifndef DEBUG 388 enable_rc_conf(); 389 add_rc_conf("wscons=YES\n"); 390 391 # if defined(__i386__) && defined(SET_KERNEL_TINY) 392 /* 393 * For GENERIC_TINY, do not enable any extra screens or wsmux. 394 * Otherwise, run getty on 4 VTs. 395 */ 396 if (get_kernel_set() == SET_KERNEL_TINY) 397 run_program(RUN_CHROOT, 398 "sed -an -e '/^screen/s/^/#/;/^mux/s/^/#/;" 399 "H;$!d;g;w /etc/wscons.conf' /etc/wscons.conf"); 400 else 401 # endif 402 run_program(RUN_CHROOT, 403 "sed -an -e '/^ttyE[1-9]/s/off/on/;" 404 "H;$!d;g;w /etc/ttys' /etc/ttys"); 405 406 #endif 407 408 /* 409 * Get console device, should either be ttyE0 or tty0n. 410 * Too hard to double check, so just 'know' the device numbers. 411 */ 412 len = sizeof condev; 413 if (sysctl(conmib, nelem(conmib), &condev, &len, NULL, 0) != -1 414 && (condev & ~3) != 0x800) { 415 416 /* 417 * Current console is not com*, assume ttyE*. 418 * Modify /etc/ttys to use wsvt25 for all ports. 419 */ 420 421 run_program(RUN_CHROOT, 422 "sed -an -e 's/vt100/wsvt25/g;" 423 "H;$!d;g;w /etc/ttys' /etc/ttys"); 424 } 425 } 426 427 int 428 md_pre_update(void) 429 { 430 return 1; 431 } 432 433 /* Upgrade support */ 434 int 435 md_update(void) 436 { 437 md_post_newfs(); 438 md_upgrade_mbrtype(); 439 return 1; 440 } 441 442 int 443 md_check_mbr(mbr_info_t *mbri) 444 { 445 return 2; 446 } 447 448 int 449 md_mbr_use_wholedisk(mbr_info_t *mbri) 450 { 451 return mbr_use_wholedisk(mbri); 452 } 453 454 static int 455 get_bios_info(char *dev) 456 { 457 static struct disklist *disklist = NULL; 458 static int mib[2] = {CTL_MACHDEP, CPU_DISKINFO}; 459 int i; 460 size_t len; 461 struct biosdisk_info *bip; 462 struct nativedisk_info *nip = NULL, *nat; 463 int cyl, head; 464 daddr_t sec; 465 466 if (disklist == NULL) { 467 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) 468 goto nogeom; 469 disklist = malloc(len); 470 if (disklist == NULL) { 471 fprintf(stderr, "Out of memory\n"); 472 return -1; 473 } 474 sysctl(mib, 2, disklist, &len, NULL, 0); 475 } 476 477 for (i = 0; i < disklist->dl_nnativedisks; i++) { 478 nat = &disklist->dl_nativedisks[i]; 479 if (!strcmp(dev, nat->ni_devname)) { 480 nip = nat; 481 break; 482 } 483 } 484 if (nip == NULL || nip->ni_nmatches == 0) { 485 nogeom: 486 if (nip != NULL) 487 msg_display(MSG_nobiosgeom, pm->dlcyl, pm->dlhead, pm->dlsec); 488 if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0 489 && nip != NULL) 490 msg_display_add(MSG_biosguess, cyl, head, sec); 491 biosdisk = NULL; 492 } else { 493 guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec); 494 if (nip->ni_nmatches == 1) { 495 bip = &disklist->dl_biosdisks[nip->ni_biosmatches[0]]; 496 msg_display(MSG_onebiosmatch); 497 msg_table_add(MSG_onebiosmatch_header); 498 msg_table_add(MSG_onebiosmatch_row, bip->bi_dev, 499 bip->bi_cyl, bip->bi_head, bip->bi_sec, 500 (unsigned)bip->bi_lbasecs, 501 (unsigned)(bip->bi_lbasecs / (1000000000 / 512))); 502 msg_display_add(MSG_biosgeom_advise); 503 biosdisk = bip; 504 process_menu(MENU_biosonematch, &biosdisk); 505 } else { 506 msg_display(MSG_biosmultmatch); 507 msg_table_add(MSG_biosmultmatch_header); 508 for (i = 0; i < nip->ni_nmatches; i++) { 509 bip = &disklist->dl_biosdisks[ 510 nip->ni_biosmatches[i]]; 511 msg_table_add(MSG_biosmultmatch_row, i, 512 bip->bi_dev, bip->bi_cyl, bip->bi_head, 513 bip->bi_sec, (unsigned)bip->bi_lbasecs, 514 (unsigned)bip->bi_lbasecs/(1000000000/512)); 515 } 516 process_menu(MENU_biosmultmatch, &i); 517 if (i == -1) 518 biosdisk = NULL; 519 else 520 biosdisk = &disklist->dl_biosdisks[ 521 nip->ni_biosmatches[i]]; 522 } 523 } 524 if (biosdisk == NULL) { 525 if (nip != NULL) { 526 set_bios_geom(cyl, head, sec); 527 } else { 528 bcyl = cyl; 529 bhead = head; 530 bsec = sec; 531 } 532 } else { 533 bcyl = biosdisk->bi_cyl; 534 bhead = biosdisk->bi_head; 535 bsec = biosdisk->bi_sec; 536 } 537 return 0; 538 } 539 540 static int 541 mbr_root_above_chs(void) 542 { 543 return pm->ptstart + DEFROOTSIZE * (MEG / 512) 544 >= (unsigned long)bcyl * bhead * bsec; 545 } 546 547 static void 548 md_upgrade_mbrtype(void) 549 { 550 struct mbr_partition *mbrp; 551 int i, netbsdpart = -1, oldbsdpart = -1, oldbsdcount = 0; 552 553 if (pm->no_mbr) 554 return; 555 556 if (read_mbr(pm->diskdev, &mbr) < 0) 557 return; 558 559 mbrp = &mbr.mbr.mbr_parts[0]; 560 561 for (i = 0; i < MBR_PART_COUNT; i++) { 562 if (mbrp[i].mbrp_type == MBR_PTYPE_386BSD) { 563 oldbsdpart = i; 564 oldbsdcount++; 565 } else if (mbrp[i].mbrp_type == MBR_PTYPE_NETBSD) 566 netbsdpart = i; 567 } 568 569 if (netbsdpart == -1 && oldbsdcount == 1) { 570 mbrp[oldbsdpart].mbrp_type = MBR_PTYPE_NETBSD; 571 write_mbr(pm->diskdev, &mbr, 0); 572 } 573 } 574 575 /* 576 * Read MBR code from a file. 577 * The existing partition table and bootselect configuration is kept. 578 */ 579 static int 580 md_read_bootcode(const char *path, struct mbr_sector *mbrs) 581 { 582 int fd; 583 struct stat st; 584 size_t len; 585 struct mbr_sector new_mbr; 586 uint32_t dsn; 587 588 fd = open(path, O_RDONLY); 589 if (fd < 0) 590 return -1; 591 592 if (fstat(fd, &st) < 0 || st.st_size != sizeof *mbrs) { 593 close(fd); 594 return -1; 595 } 596 597 if (read(fd, &new_mbr, sizeof new_mbr) != sizeof new_mbr) { 598 close(fd); 599 return -1; 600 } 601 close(fd); 602 603 if (new_mbr.mbr_bootsel_magic != htole16(MBR_BS_MAGIC)) 604 return -1; 605 606 if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) { 607 len = offsetof(struct mbr_sector, mbr_bootsel); 608 } else 609 len = offsetof(struct mbr_sector, mbr_parts); 610 611 /* Preserve the 'drive serial number' - especially for Vista */ 612 dsn = mbrs->mbr_dsn; 613 memcpy(mbrs, &new_mbr, len); 614 mbrs->mbr_dsn = dsn; 615 616 /* Keep flags from object file - indicate the properties */ 617 mbrs->mbr_bootsel.mbrbs_flags = new_mbr.mbr_bootsel.mbrbs_flags; 618 mbrs->mbr_magic = htole16(MBR_MAGIC); 619 620 return 0; 621 } 622 623 static unsigned int 624 get_bootmodel(void) 625 { 626 #if defined(__i386__) 627 struct utsname ut; 628 #ifdef DEBUG 629 char *envstr; 630 631 envstr = getenv("BOOTMODEL"); 632 if (envstr != NULL) 633 return atoi(envstr); 634 #endif 635 636 if (uname(&ut) < 0) 637 ut.version[0] = 0; 638 639 #if defined(SET_KERNEL_TINY) 640 if (strstr(ut.version, "TINY") != NULL) 641 return SET_KERNEL_TINY; 642 #endif 643 #if defined(SET_KERNEL_PS2) 644 if (strstr(ut.version, "PS2") != NULL) 645 return SET_KERNEL_PS2; 646 #endif 647 #endif 648 return SET_KERNEL_GENERIC; 649 } 650 651 652 int 653 md_pre_mount() 654 { 655 return 0; 656 } 657