1 /* $NetBSD: md.c,v 1.123 2009/09/19 14:57:28 abs Exp $ */
4 * Copyright 1997 Piermont Information Systems Inc.
7 * Based on code written by Philip A. Nelson for Piermont Information
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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 for the NetBSD Project by
21 * Piermont Information Systems Inc.
22 * 4. The name of Piermont Information Systems Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
26 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
30 * 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
36 * THE POSSIBILITY OF SUCH DAMAGE.
39 /* md.c -- i386 machine specific routines - also used by amd64 */
41 #include <sys/param.h>
42 #include <sys/sysctl.h>
44 #include <sys/utsname.h>
45 #include <sys/types.h>
47 #include <machine/cpu.h>
57 #include "menu_defs.h"
59 #ifdef NO_LBA_READS /* for testing */
60 #undef BIFLAG_EXTINT13
61 #define BIFLAG_EXTINT13 0
64 static struct biosdisk_info
*biosdisk
= NULL
;
68 static int get_bios_info(char *);
69 static int mbr_root_above_chs(void);
70 static void md_upgrade_mbrtype(void);
71 static int md_read_bootcode(const char *, struct mbr_sector
*);
72 static unsigned int get_bootmodel(void);
80 md_init_set_status(int minimal
)
84 /* Default to install same type of kernel as we are running */
85 set_kernel_set(get_bootmodel());
92 struct mbr_partition
*p
;
96 #define ACTIVE_FOUND 0x0100
97 #define NETBSD_ACTIVE 0x0200
98 #define NETBSD_NAMED 0x0400
99 #define ACTIVE_NAMED 0x0800
104 if (read_mbr(diskdev
, &mbr
) < 0)
105 memset(&mbr
.mbr
, 0, sizeof mbr
.mbr
- 2);
106 get_bios_info(diskdev
);
109 if (edit_mbr(&mbr
) == 0)
113 if (biosdisk
!= NULL
&& (biosdisk
->bi_flags
& BIFLAG_EXTINT13
) == 0) {
114 if (mbr_root_above_chs()) {
115 msg_display(MSG_partabovechs
);
116 process_menu(MENU_noyes
, NULL
);
119 /* The user is shooting themselves in the foot here...*/
121 root_limit
= bcyl
* bhead
* bsec
;
125 * Ensure the install partition (at sector ptstart) and the active
126 * partition are bootable.
127 * Determine whether the bootselect code is needed.
128 * Note that MBR_BS_NEWMBR is always set, so we ignore it!
132 for (ext
= &mbr
; ext
!= NULL
; ext
= ext
->extended
) {
133 p
= ext
->mbr
.mbr_parts
;
134 for (i
= 0; i
< MBR_PART_COUNT
; p
++, i
++) {
135 if (p
->mbrp_flag
== MBR_PFLAG_ACTIVE
) {
137 if (ext
->sector
+ p
->mbrp_start
== ptstart
)
140 if (ext
->mbrb
.mbrbs_nametab
[i
][0] == 0) {
141 /* No bootmenu label... */
142 if (ext
->sector
== 0)
144 if (ext
->sector
+ p
->mbrp_start
== ptstart
)
146 * Have installed into an extended ptn
147 * force name & bootsel...
152 /* Partition has a bootmenu label... */
153 if (ext
->sector
!= 0)
155 if (ext
->sector
+ p
->mbrp_start
== ptstart
)
157 else if (p
->mbrp_flag
== MBR_PFLAG_ACTIVE
)
163 if (!(fl
& ACTIVE_FOUND
))
165 if (fl
& NETBSD_NAMED
&& fl
& NETBSD_ACTIVE
)
168 if ((names
> 0 || !(fl
& NETBSD_ACTIVE
)) &&
169 (!(fl
& NETBSD_NAMED
) || !(fl
& ACTIVE_NAMED
))) {
171 * There appear to be multiple bootable partitions, but they
172 * don't all have bootmenu texts.
174 msg_display(MSG_missing_bootmenu_text
);
175 process_menu(MENU_yesno
, NULL
);
180 if ((fl
& MBR_BS_EXTLBA
) &&
181 (biosdisk
== NULL
|| !(biosdisk
->bi_flags
& BIFLAG_EXTINT13
))) {
182 /* Need unsupported LBA reads to read boot sectors */
183 msg_display(MSG_no_extended_bootmenu
);
184 process_menu(MENU_noyes
, NULL
);
189 /* Sort out the name of the mbr code we need */
190 if (names
> 0 || fl
& (NETBSD_NAMED
| ACTIVE_NAMED
)) {
191 /* Need bootselect code */
193 bootcode
= fl
& MBR_BS_EXTLBA
? _PATH_BOOTEXT
: _PATH_BOOTSEL
;
195 bootcode
= _PATH_MBR
;
197 fl
&= MBR_BS_ACTIVE
| MBR_BS_EXTLBA
;
199 /* Look at what is installed */
200 ofl
= mbr
.mbrb
.mbrbs_flags
;
202 /* Check there is some bootcode at all... */
203 if (mbr
.mbr
.mbr_magic
!= htole16(MBR_MAGIC
) ||
204 mbr
.mbr
.mbr_jmpboot
[0] == 0 ||
205 mbr_root_above_chs())
206 /* Existing won't do, force update */
209 ofl
= mbr
.oflags
& (MBR_BS_ACTIVE
| MBR_BS_EXTLBA
);
211 if (fl
& ~ofl
|| (fl
== 0 && ofl
& MBR_BS_ACTIVE
)) {
212 /* Existing boot code isn't the right one... */
213 if (fl
& MBR_BS_ACTIVE
)
214 msg_display(MSG_installbootsel
);
216 msg_display(MSG_installmbr
);
218 /* Existing code would (probably) be ok */
219 msg_display(MSG_updatembr
);
221 process_menu(MENU_yesno
, NULL
);
223 /* User doesn't want to update mbr code */
226 if (md_read_bootcode(bootcode
, &mbr
.mbr
) == 0)
227 /* update suceeded - to memory copy */
230 /* This shouldn't happen since the files are in the floppy fs... */
231 msg_display("Can't find %s", bootcode
);
232 process_menu(MENU_yesno
, NULL
);
238 * md back-end code for menu-driven BSD disklabel editor.
241 md_make_bsd_partitions(void)
243 return make_bsd_partitions();
247 * any additional partition validation
250 md_check_partitions(void)
255 /* check we have boot code for the root partition type */
256 bootxx
= bootxx_name();
257 rval
= access(bootxx
, R_OK
);
261 process_menu(MENU_ok
, deconst(MSG_No_Bootcode
));
266 * hook called before writing new disklabel.
269 md_pre_disklabel(void)
274 msg_display(MSG_dofdisk
);
276 /* write edited MBR onto disk. */
277 if (write_mbr(diskdev
, &mbr
, 1) != 0) {
278 msg_display(MSG_wmbrfail
);
279 process_menu(MENU_ok
, NULL
);
286 * hook called after writing disklabel to new target disk.
289 md_post_disklabel(void)
291 if (get_ramsize() <= 32)
292 set_swap(diskdev
, bsdlabel
);
298 * hook called after upgrade() or install() has finished setting
299 * up the target disk but immediately before the user is given the
300 * ``disks are now set up'' message.
308 char bootxx
[8192 + 4];
309 char *bootxx_filename
;
311 * XXX - should either find some way to pull this automatically
312 * from sys/arch/i386/stand/lib/boot_params.S, or just bite the
313 * bullet and include /sbin/installboot on the ramdisk
315 static struct x86_boot_params boottype
=
316 {sizeof boottype
, 0, 5, 0, 9600, { '\0' }, "", 0};
317 static int conmib
[] = {CTL_MACHDEP
, CPU_CONSDEV
};
320 #define bp (*(struct x86_boot_params *)(bootxx + 512 * 2 + 8))
323 * Get console device, should either be ttyE0 or tty0n.
324 * Too hard to double check, so just 'know' the device numbers.
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
;
336 process_menu(MENU_getboottype
, &boottype
);
337 msg_display(MSG_dobootblks
, diskdev
);
338 if (bp
.bp_consdev
== ~0u)
341 ret
= cp_to_target("/usr/mdec/boot", "/boot");
345 /* Copy bootstrap in by hand - /sbin/installboot explodes ramdisks */
348 snprintf(bootxx
, sizeof bootxx
, "/dev/r%s%c", diskdev
, 'a' + rootpart
);
349 td
= open(bootxx
, O_RDWR
, 0);
350 bootxx_filename
= bootxx_name();
351 if (bootxx_filename
!= NULL
) {
352 sd
= open(bootxx_filename
, O_RDONLY
);
353 free(bootxx_filename
);
356 if (td
== -1 || sd
== -1)
358 len
= read(sd
, bootxx
, sizeof bootxx
);
359 if (len
< 2048 || len
> 8192)
362 if (*(uint32_t *)(bootxx
+ 512 * 2 + 4) != X86_BOOT_MAGIC_1
)
365 boottype
.bp_length
= bp
.bp_length
;
366 memcpy(&bp
, &boottype
, min(boottype
.bp_length
, sizeof boottype
));
368 if (pwrite(td
, bootxx
, 512, 0) != 512)
371 if (pwrite(td
, bootxx
+ 512 * 2, len
, 2 * (off_t
)512) - len
!= 0)
383 md_post_extract(void)
389 md_cleanup_install(void)
393 add_rc_conf("wscons=YES\n");
395 # if defined(__i386__) && defined(SET_KERNEL_TINY)
397 * For GENERIC_TINY, do not enable any extra screens or wsmux.
398 * Otherwise, run getty on 4 VTs.
400 if (get_kernel_set() == SET_KERNEL_TINY
)
401 run_program(RUN_CHROOT
,
402 "sed -an -e '/^screen/s/^/#/;/^mux/s/^/#/;"
403 "H;$!d;g;w /etc/wscons.conf' /etc/wscons.conf");
406 run_program(RUN_CHROOT
,
407 "sed -an -e '/^ttyE[1-9]/s/off/on/;"
408 "H;$!d;g;w /etc/ttys' /etc/ttys");
416 if (get_ramsize() <= 8)
417 set_swap(diskdev
, NULL
);
421 /* Upgrade support */
426 md_upgrade_mbrtype();
431 md_check_mbr(mbr_info_t
*mbri
)
437 md_mbr_use_wholedisk(mbr_info_t
*mbri
)
439 return mbr_use_wholedisk(mbri
);
443 get_bios_info(char *dev
)
445 static struct disklist
*disklist
= NULL
;
446 static int mib
[2] = {CTL_MACHDEP
, CPU_DISKINFO
};
449 struct biosdisk_info
*bip
;
450 struct nativedisk_info
*nip
= NULL
, *nat
;
454 if (disklist
== NULL
) {
455 if (sysctl(mib
, 2, NULL
, &len
, NULL
, 0) < 0)
457 disklist
= malloc(len
);
458 if (disklist
== NULL
) {
459 fprintf(stderr
, "Out of memory\n");
462 sysctl(mib
, 2, disklist
, &len
, NULL
, 0);
465 for (i
= 0; i
< disklist
->dl_nnativedisks
; i
++) {
466 nat
= &disklist
->dl_nativedisks
[i
];
467 if (!strcmp(dev
, nat
->ni_devname
)) {
472 if (nip
== NULL
|| nip
->ni_nmatches
== 0) {
474 msg_display(MSG_nobiosgeom
, dlcyl
, dlhead
, dlsec
);
475 if (guess_biosgeom_from_mbr(&mbr
, &cyl
, &head
, &sec
) >= 0)
476 msg_display_add(MSG_biosguess
, cyl
, head
, sec
);
479 guess_biosgeom_from_mbr(&mbr
, &cyl
, &head
, &sec
);
480 if (nip
->ni_nmatches
== 1) {
481 bip
= &disklist
->dl_biosdisks
[nip
->ni_biosmatches
[0]];
482 msg_display(MSG_onebiosmatch
);
483 msg_table_add(MSG_onebiosmatch_header
);
484 msg_table_add(MSG_onebiosmatch_row
, bip
->bi_dev
,
485 bip
->bi_cyl
, bip
->bi_head
, bip
->bi_sec
,
486 (unsigned)bip
->bi_lbasecs
,
487 (unsigned)(bip
->bi_lbasecs
/ (1000000000 / 512)));
488 msg_display_add(MSG_biosgeom_advise
);
490 process_menu(MENU_biosonematch
, &biosdisk
);
492 msg_display(MSG_biosmultmatch
);
493 msg_table_add(MSG_biosmultmatch_header
);
494 for (i
= 0; i
< nip
->ni_nmatches
; i
++) {
495 bip
= &disklist
->dl_biosdisks
[
496 nip
->ni_biosmatches
[i
]];
497 msg_table_add(MSG_biosmultmatch_row
, i
,
498 bip
->bi_dev
, bip
->bi_cyl
, bip
->bi_head
,
499 bip
->bi_sec
, (unsigned)bip
->bi_lbasecs
,
500 (unsigned)bip
->bi_lbasecs
/(1000000000/512));
502 process_menu(MENU_biosmultmatch
, &i
);
506 biosdisk
= &disklist
->dl_biosdisks
[
507 nip
->ni_biosmatches
[i
]];
510 if (biosdisk
== NULL
)
511 set_bios_geom(cyl
, head
, sec
);
513 bcyl
= biosdisk
->bi_cyl
;
514 bhead
= biosdisk
->bi_head
;
515 bsec
= biosdisk
->bi_sec
;
521 mbr_root_above_chs(void)
523 return ptstart
+ DEFROOTSIZE
* (MEG
/ 512) >= bcyl
* bhead
* bsec
;
527 md_upgrade_mbrtype(void)
529 struct mbr_partition
*mbrp
;
530 int i
, netbsdpart
= -1, oldbsdpart
= -1, oldbsdcount
= 0;
535 if (read_mbr(diskdev
, &mbr
) < 0)
538 mbrp
= &mbr
.mbr
.mbr_parts
[0];
540 for (i
= 0; i
< MBR_PART_COUNT
; i
++) {
541 if (mbrp
[i
].mbrp_type
== MBR_PTYPE_386BSD
) {
544 } else if (mbrp
[i
].mbrp_type
== MBR_PTYPE_NETBSD
)
548 if (netbsdpart
== -1 && oldbsdcount
== 1) {
549 mbrp
[oldbsdpart
].mbrp_type
= MBR_PTYPE_NETBSD
;
550 write_mbr(diskdev
, &mbr
, 0);
555 * Read MBR code from a file.
556 * The existing partition table and bootselect configuration is kept.
559 md_read_bootcode(const char *path
, struct mbr_sector
*mbrs
)
564 struct mbr_sector new_mbr
;
567 fd
= open(path
, O_RDONLY
);
571 if (fstat(fd
, &st
) < 0 || st
.st_size
!= sizeof *mbrs
) {
576 if (read(fd
, &new_mbr
, sizeof new_mbr
) != sizeof new_mbr
) {
582 if (new_mbr
.mbr_bootsel_magic
!= htole16(MBR_BS_MAGIC
))
585 if (mbrs
->mbr_bootsel_magic
== htole16(MBR_BS_MAGIC
)) {
586 len
= offsetof(struct mbr_sector
, mbr_bootsel
);
588 len
= offsetof(struct mbr_sector
, mbr_parts
);
590 /* Preserve the 'drive serial number' - especially for Vista */
592 memcpy(mbrs
, &new_mbr
, len
);
595 /* Keep flags from object file - indicate the properties */
596 mbrs
->mbr_bootsel
.mbrbs_flags
= new_mbr
.mbr_bootsel
.mbrbs_flags
;
597 mbrs
->mbr_magic
= htole16(MBR_MAGIC
);
605 #if defined(__i386__)
610 envstr
= getenv("BOOTMODEL");
618 #if defined(SET_KERNEL_TINY)
619 if (strstr(ut
.version
, "TINY") != NULL
)
620 return SET_KERNEL_TINY
;
622 #if defined(SET_KERNEL_PS2)
623 if (strstr(ut
.version
, "PS2") != NULL
)
624 return SET_KERNEL_PS2
;
627 return SET_KERNEL_GENERIC
;