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