1/* $NetBSD: md.c,v 1.129 2011/11/04 11:27:03 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 49#include "defs.h" 50#include "md.h" 51#include "endian.h" 52#include "msg_defs.h" 53#include "menu_defs.h" 54 55#ifdef NO_LBA_READS /* for testing */ 56#undef BIFLAG_EXTINT13 57#define BIFLAG_EXTINT13 0 58#endif 59 60static struct biosdisk_info *biosdisk = NULL; 61 62/* prototypes */ 63 64static int get_bios_info(char *); 65static int mbr_root_above_chs(void); 66static void md_upgrade_mbrtype(void); 67static int md_read_bootcode(const char *, struct mbr_sector *); 68static unsigned int get_bootmodel(void); 69 70void 71md_init(void) 72{ 73} 74 75void 76md_init_set_status(int flags) 77{ 78 (void)flags; 79 80 /* Default to install same type of kernel as we are running */ 81 set_kernel_set(get_bootmodel()); 82} 83 84int 85md_get_info(void) 86{ 87 mbr_info_t *ext; 88 struct mbr_partition *p; 89 const char *bootcode; 90 int i; 91 int names, fl, ofl; 92#define ACTIVE_FOUND 0x0100 93#define NETBSD_ACTIVE 0x0200 94#define NETBSD_NAMED 0x0400 95#define ACTIVE_NAMED 0x0800 96 97 if (no_mbr) 98 return 1; 99 100 if (read_mbr(diskdev, &mbr) < 0) 101 memset(&mbr.mbr, 0, sizeof mbr.mbr - 2); 102 get_bios_info(diskdev); 103 104edit: 105 if (edit_mbr(&mbr) == 0) 106 return 0; 107 108 root_limit = 0; 109 if (biosdisk != NULL && (biosdisk->bi_flags & BIFLAG_EXTINT13) == 0) { 110 if (mbr_root_above_chs()) { 111 msg_display(MSG_partabovechs); 112 process_menu(MENU_noyes, NULL); 113 if (!yesno) 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 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 == 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 == 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 == 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 process_menu(MENU_yesno, NULL); 172 if (yesno) 173 goto edit; 174 } 175 176 if ((fl & MBR_BS_EXTLBA) && 177 (biosdisk == NULL || !(biosdisk->bi_flags & BIFLAG_EXTINT13))) { 178 /* Need unsupported LBA reads to read boot sectors */ 179 msg_display(MSG_no_extended_bootmenu); 180 process_menu(MENU_noyes, NULL); 181 if (!yesno) 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 process_menu(MENU_yesno, NULL); 218 if (!yesno) 219 /* User doesn't want to update mbr code */ 220 return 1; 221 222 if (md_read_bootcode(bootcode, &mbr.mbr) == 0) 223 /* update suceeded - to memory copy */ 224 return 1; 225 226 /* This shouldn't happen since the files are in the floppy fs... */ 227 msg_display("Can't find %s", bootcode); 228 process_menu(MENU_yesno, NULL); 229 230 return 1; 231} 232 233/* 234 * md back-end code for menu-driven BSD disklabel editor. 235 */ 236int 237md_make_bsd_partitions(void) 238{ 239 return make_bsd_partitions(); 240} 241 242/* 243 * any additional partition validation 244 */ 245int 246md_check_partitions(void) 247{ 248 int rval; 249 char *bootxx; 250 251 /* check we have boot code for the root partition type */ 252 bootxx = bootxx_name(); 253 rval = access(bootxx, R_OK); 254 free(bootxx); 255 if (rval == 0) 256 return 1; 257 process_menu(MENU_ok, deconst(MSG_No_Bootcode)); 258 return 0; 259} 260 261/* 262 * hook called before writing new disklabel. 263 */ 264int 265md_pre_disklabel(void) 266{ 267 if (no_mbr) 268 return 0; 269 270 msg_display(MSG_dofdisk); 271 272 /* write edited MBR onto disk. */ 273 if (write_mbr(diskdev, &mbr, 1) != 0) { 274 msg_display(MSG_wmbrfail); 275 process_menu(MENU_ok, NULL); 276 return 1; 277 } 278 return 0; 279} 280 281/* 282 * hook called after writing disklabel to new target disk. 283 */ 284int 285md_post_disklabel(void) 286{ 287 if (get_ramsize() <= 32) 288 set_swap(diskdev, bsdlabel); 289 290 return 0; 291} 292 293/* 294 * hook called after upgrade() or install() has finished setting 295 * up the target disk but immediately before the user is given the 296 * ``disks are now set up'' message. 297 */ 298int 299md_post_newfs(void) 300{ 301 int ret; 302 size_t len; 303 int td, sd; 304 char bootxx[8192 + 4]; 305 char *bootxx_filename; 306 /* 307 * XXX - should either find some way to pull this automatically 308 * from sys/arch/i386/stand/lib/boot_params.S, or just bite the 309 * bullet and include /sbin/installboot on the ramdisk 310 */ 311 static struct x86_boot_params boottype = 312 {sizeof boottype, 0, 5, 0, 9600, { '\0' }, "", 0}; 313 static int conmib[] = {CTL_MACHDEP, CPU_CONSDEV}; 314 struct termios t; 315 dev_t condev; 316#define bp (*(struct x86_boot_params *)(bootxx + 512 * 2 + 8)) 317 318 /* 319 * Get console device, should either be ttyE0 or tty0n. 320 * Too hard to double check, so just 'know' the device numbers. 321 */ 322 len = sizeof condev; 323 if (sysctl(conmib, nelem(conmib), &condev, &len, NULL, 0) != -1 324 && (condev & ~3) == 0x800) { 325 /* Motherboard serial port */ 326 boottype.bp_consdev = (condev & 3) + 1; 327 /* Defaulting the baud rate to that of stdin should suffice */ 328 if (tcgetattr(0, &t) != -1) 329 boottype.bp_conspeed = t.c_ispeed; 330 } 331 332 process_menu(MENU_getboottype, &boottype); 333 msg_display(MSG_dobootblks, diskdev); 334 if (bp.bp_consdev == ~0u) 335 return 0; 336 337 ret = cp_to_target("/usr/mdec/boot", "/boot"); 338 if (ret) 339 return ret; 340 341 /* Copy bootstrap in by hand - /sbin/installboot explodes ramdisks */ 342 ret = 1; 343 344 snprintf(bootxx, sizeof bootxx, "/dev/r%s%c", diskdev, 'a' + rootpart); 345 td = open(bootxx, O_RDWR, 0); 346 bootxx_filename = bootxx_name(); 347 if (bootxx_filename != NULL) { 348 sd = open(bootxx_filename, O_RDONLY); 349 free(bootxx_filename); 350 } else 351 sd = -1; 352 if (td == -1 || sd == -1) 353 goto bad_bootxx; 354 len = read(sd, bootxx, sizeof bootxx); 355 if (len < 2048 || len > 8192) 356 goto bad_bootxx; 357 358 if (*(uint32_t *)(bootxx + 512 * 2 + 4) != X86_BOOT_MAGIC_1) 359 goto bad_bootxx; 360 361 boottype.bp_length = bp.bp_length; 362 memcpy(&bp, &boottype, min(boottype.bp_length, sizeof boottype)); 363 364 if (pwrite(td, bootxx, 512, 0) != 512) 365 goto bad_bootxx; 366 len -= 512 * 2; 367 if (pwrite(td, bootxx + 512 * 2, len, 2 * (off_t)512) - len != 0) 368 goto bad_bootxx; 369 ret = 0; 370 371 bad_bootxx: 372 close(td); 373 close(sd); 374 375 return ret; 376} 377 378int 379md_post_extract(void) 380{ 381 return 0; 382} 383 384void 385md_cleanup_install(void) 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 409int 410md_pre_update(void) 411{ 412 if (get_ramsize() <= 8) 413 set_swap(diskdev, NULL); 414 return 1; 415} 416 417/* Upgrade support */ 418int 419md_update(void) 420{ 421 md_post_newfs(); 422 md_upgrade_mbrtype(); 423 return 1; 424} 425 426int 427md_check_mbr(mbr_info_t *mbri) 428{ 429 return 2; 430} 431 432int 433md_mbr_use_wholedisk(mbr_info_t *mbri) 434{ 435 return mbr_use_wholedisk(mbri); 436} 437 438static int 439get_bios_info(char *dev) 440{ 441 static struct disklist *disklist = NULL; 442 static int mib[2] = {CTL_MACHDEP, CPU_DISKINFO}; 443 int i; 444 size_t len; 445 struct biosdisk_info *bip; 446 struct nativedisk_info *nip = NULL, *nat; 447 int cyl, head; 448 daddr_t sec; 449 450 if (disklist == NULL) { 451 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) 452 goto nogeom; 453 disklist = malloc(len); 454 if (disklist == NULL) { 455 fprintf(stderr, "Out of memory\n"); 456 return -1; 457 } 458 sysctl(mib, 2, disklist, &len, NULL, 0); 459 } 460 461 for (i = 0; i < disklist->dl_nnativedisks; i++) { 462 nat = &disklist->dl_nativedisks[i]; 463 if (!strcmp(dev, nat->ni_devname)) { 464 nip = nat; 465 break; 466 } 467 } 468 if (nip == NULL || nip->ni_nmatches == 0) { 469nogeom: 470 if (nip != NULL) 471 msg_display(MSG_nobiosgeom, dlcyl, dlhead, dlsec); 472 if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0 473 && nip != NULL) 474 msg_display_add(MSG_biosguess, cyl, head, sec); 475 biosdisk = NULL; 476 } else { 477 guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec); 478 if (nip->ni_nmatches == 1) { 479 bip = &disklist->dl_biosdisks[nip->ni_biosmatches[0]]; 480 msg_display(MSG_onebiosmatch); 481 msg_table_add(MSG_onebiosmatch_header); 482 msg_table_add(MSG_onebiosmatch_row, bip->bi_dev, 483 bip->bi_cyl, bip->bi_head, bip->bi_sec, 484 (unsigned)bip->bi_lbasecs, 485 (unsigned)(bip->bi_lbasecs / (1000000000 / 512))); 486 msg_display_add(MSG_biosgeom_advise); 487 biosdisk = bip; 488 process_menu(MENU_biosonematch, &biosdisk); 489 } else { 490 msg_display(MSG_biosmultmatch); 491 msg_table_add(MSG_biosmultmatch_header); 492 for (i = 0; i < nip->ni_nmatches; i++) { 493 bip = &disklist->dl_biosdisks[ 494 nip->ni_biosmatches[i]]; 495 msg_table_add(MSG_biosmultmatch_row, i, 496 bip->bi_dev, bip->bi_cyl, bip->bi_head, 497 bip->bi_sec, (unsigned)bip->bi_lbasecs, 498 (unsigned)bip->bi_lbasecs/(1000000000/512)); 499 } 500 process_menu(MENU_biosmultmatch, &i); 501 if (i == -1) 502 biosdisk = NULL; 503 else 504 biosdisk = &disklist->dl_biosdisks[ 505 nip->ni_biosmatches[i]]; 506 } 507 } 508 if (biosdisk == NULL) { 509 if (nip != NULL) { 510 set_bios_geom(cyl, head, sec); 511 } else { 512 bcyl = cyl; 513 bhead = head; 514 bsec = sec; 515 } 516 } else { 517 bcyl = biosdisk->bi_cyl; 518 bhead = biosdisk->bi_head; 519 bsec = biosdisk->bi_sec; 520 } 521 return 0; 522} 523 524static int 525mbr_root_above_chs(void) 526{ 527 return ptstart + DEFROOTSIZE * (MEG / 512) >= bcyl * bhead * bsec; 528} 529 530static void 531md_upgrade_mbrtype(void) 532{ 533 struct mbr_partition *mbrp; 534 int i, netbsdpart = -1, oldbsdpart = -1, oldbsdcount = 0; 535 536 if (no_mbr) 537 return; 538 539 if (read_mbr(diskdev, &mbr) < 0) 540 return; 541 542 mbrp = &mbr.mbr.mbr_parts[0]; 543 544 for (i = 0; i < MBR_PART_COUNT; i++) { 545 if (mbrp[i].mbrp_type == MBR_PTYPE_386BSD) { 546 oldbsdpart = i; 547 oldbsdcount++; 548 } else if (mbrp[i].mbrp_type == MBR_PTYPE_NETBSD) 549 netbsdpart = i; 550 } 551 552 if (netbsdpart == -1 && oldbsdcount == 1) { 553 mbrp[oldbsdpart].mbrp_type = MBR_PTYPE_NETBSD; 554 write_mbr(diskdev, &mbr, 0); 555 } 556} 557 558/* 559 * Read MBR code from a file. 560 * The existing partition table and bootselect configuration is kept. 561 */ 562static int 563md_read_bootcode(const char *path, struct mbr_sector *mbrs) 564{ 565 int fd; 566 struct stat st; 567 size_t len; 568 struct mbr_sector new_mbr; 569 uint32_t dsn; 570 571 fd = open(path, O_RDONLY); 572 if (fd < 0) 573 return -1; 574 575 if (fstat(fd, &st) < 0 || st.st_size != sizeof *mbrs) { 576 close(fd); 577 return -1; 578 } 579 580 if (read(fd, &new_mbr, sizeof new_mbr) != sizeof new_mbr) { 581 close(fd); 582 return -1; 583 } 584 close(fd); 585 586 if (new_mbr.mbr_bootsel_magic != htole16(MBR_BS_MAGIC)) 587 return -1; 588 589 if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) { 590 len = offsetof(struct mbr_sector, mbr_bootsel); 591 } else 592 len = offsetof(struct mbr_sector, mbr_parts); 593 594 /* Preserve the 'drive serial number' - especially for Vista */ 595 dsn = mbrs->mbr_dsn; 596 memcpy(mbrs, &new_mbr, len); 597 mbrs->mbr_dsn = dsn; 598 599 /* Keep flags from object file - indicate the properties */ 600 mbrs->mbr_bootsel.mbrbs_flags = new_mbr.mbr_bootsel.mbrbs_flags; 601 mbrs->mbr_magic = htole16(MBR_MAGIC); 602 603 return 0; 604} 605 606static unsigned int 607get_bootmodel(void) 608{ 609#if defined(__i386__) 610 struct utsname ut; 611#ifdef DEBUG 612 char *envstr; 613 614 envstr = getenv("BOOTMODEL"); 615 if (envstr != NULL) 616 return atoi(envstr); 617#endif 618 619 if (uname(&ut) < 0) 620 ut.version[0] = 0; 621 622#if defined(SET_KERNEL_TINY) 623 if (strstr(ut.version, "TINY") != NULL) 624 return SET_KERNEL_TINY; 625#endif 626#if defined(SET_KERNEL_PS2) 627 if (strstr(ut.version, "PS2") != NULL) 628 return SET_KERNEL_PS2; 629#endif 630#endif 631 return SET_KERNEL_GENERIC; 632} 633 634 635int 636md_pre_mount() 637{ 638 return 0; 639} 640