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