md.c revision 1.13 1 /* $NetBSD: md.c,v 1.13 2018/09/15 18:04:21 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) >= bcyl * bhead * bsec;
544 }
545
546 static void
547 md_upgrade_mbrtype(void)
548 {
549 struct mbr_partition *mbrp;
550 int i, netbsdpart = -1, oldbsdpart = -1, oldbsdcount = 0;
551
552 if (pm->no_mbr)
553 return;
554
555 if (read_mbr(pm->diskdev, &mbr) < 0)
556 return;
557
558 mbrp = &mbr.mbr.mbr_parts[0];
559
560 for (i = 0; i < MBR_PART_COUNT; i++) {
561 if (mbrp[i].mbrp_type == MBR_PTYPE_386BSD) {
562 oldbsdpart = i;
563 oldbsdcount++;
564 } else if (mbrp[i].mbrp_type == MBR_PTYPE_NETBSD)
565 netbsdpart = i;
566 }
567
568 if (netbsdpart == -1 && oldbsdcount == 1) {
569 mbrp[oldbsdpart].mbrp_type = MBR_PTYPE_NETBSD;
570 write_mbr(pm->diskdev, &mbr, 0);
571 }
572 }
573
574 /*
575 * Read MBR code from a file.
576 * The existing partition table and bootselect configuration is kept.
577 */
578 static int
579 md_read_bootcode(const char *path, struct mbr_sector *mbrs)
580 {
581 int fd;
582 struct stat st;
583 size_t len;
584 struct mbr_sector new_mbr;
585 uint32_t dsn;
586
587 fd = open(path, O_RDONLY);
588 if (fd < 0)
589 return -1;
590
591 if (fstat(fd, &st) < 0 || st.st_size != sizeof *mbrs) {
592 close(fd);
593 return -1;
594 }
595
596 if (read(fd, &new_mbr, sizeof new_mbr) != sizeof new_mbr) {
597 close(fd);
598 return -1;
599 }
600 close(fd);
601
602 if (new_mbr.mbr_bootsel_magic != htole16(MBR_BS_MAGIC))
603 return -1;
604
605 if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
606 len = offsetof(struct mbr_sector, mbr_bootsel);
607 } else
608 len = offsetof(struct mbr_sector, mbr_parts);
609
610 /* Preserve the 'drive serial number' - especially for Vista */
611 dsn = mbrs->mbr_dsn;
612 memcpy(mbrs, &new_mbr, len);
613 mbrs->mbr_dsn = dsn;
614
615 /* Keep flags from object file - indicate the properties */
616 mbrs->mbr_bootsel.mbrbs_flags = new_mbr.mbr_bootsel.mbrbs_flags;
617 mbrs->mbr_magic = htole16(MBR_MAGIC);
618
619 return 0;
620 }
621
622 static unsigned int
623 get_bootmodel(void)
624 {
625 #if defined(__i386__)
626 struct utsname ut;
627 #ifdef DEBUG
628 char *envstr;
629
630 envstr = getenv("BOOTMODEL");
631 if (envstr != NULL)
632 return atoi(envstr);
633 #endif
634
635 if (uname(&ut) < 0)
636 ut.version[0] = 0;
637
638 #if defined(SET_KERNEL_TINY)
639 if (strstr(ut.version, "TINY") != NULL)
640 return SET_KERNEL_TINY;
641 #endif
642 #if defined(SET_KERNEL_PS2)
643 if (strstr(ut.version, "PS2") != NULL)
644 return SET_KERNEL_PS2;
645 #endif
646 #endif
647 return SET_KERNEL_GENERIC;
648 }
649
650
651 int
652 md_pre_mount()
653 {
654 return 0;
655 }
656