1/*	$NetBSD: md.c,v 1.11 2012/01/29 16:01:36 phx 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 -- ofppc machine specific routines */
36
37#include <sys/param.h>
38#include <sys/sysctl.h>
39#include <sys/disklabel_rdb.h>
40#include <stdio.h>
41#include <util.h>
42#include <machine/cpu.h>
43
44#include "defs.h"
45#include "md.h"
46#include "msg_defs.h"
47#include "menu_defs.h"
48#include "endian.h"
49
50static int check_rdb(void);
51static uint32_t rdbchksum(void *);
52
53/* We use MBR_PTYPE_PREP like port-prep does. */
54static int nonewfsmsdos = 0, nobootfix = 0, noprepfix=0;
55static int bootpart_fat12 = PART_BOOT_FAT12;
56static int bootpart_binfo = PART_BOOT_BINFO;
57static int bootpart_prep = PART_BOOT_PREP;
58static int bootinfo_mbr = 1;
59static int rdb_found = 0;
60
61/* bootstart/bootsize are for the fat */
62int binfostart, binfosize, bprepstart, bprepsize;
63
64void
65md_init(void)
66{
67}
68
69void
70md_init_set_status(int flags)
71{
72
73	(void)flags;
74}
75
76int
77md_get_info(void)
78{
79
80	if (check_rdb())
81		return 1;
82
83	return set_bios_geom_with_mbr_guess();
84}
85
86/*
87 * md back-end code for menu-driven BSD disklabel editor.
88 */
89int
90md_make_bsd_partitions(void)
91{
92	int i;
93	int part;
94	int maxpart = getmaxpartitions();
95	int partstart;
96	int part_raw, part_bsd;
97	int ptend;
98	int no_swap = 0;
99	partinfo *p;
100
101	if (rdb_found) {
102		/*
103		 * We found RDB partitions on the disk, which cannot be
104		 * modified by rewriting the disklabel.
105		 * So just use what we have got.
106		 */
107		for (part = 0; part < maxpart; part++) {
108			if (PI_ISBSDFS(&bsdlabel[part])) {
109				bsdlabel[part].pi_flags |=
110				    PIF_NEWFS | PIF_MOUNT;
111
112				if (part == PART_A)
113					strcpy(bsdlabel[part].pi_mount, "/");
114			}
115		}
116
117		part_bsd = part_raw = getrawpartition();
118		if (part_raw == -1)
119			part_raw = PART_C;	/* for sanity... */
120		bsdlabel[part_raw].pi_offset = 0;
121		bsdlabel[part_raw].pi_size = dlsize;
122
123		set_sizemultname_meg();
124rdb_edit_check:
125		if (edit_and_check_label(bsdlabel, maxpart, part_raw,
126		    part_bsd) == 0) {
127			msg_display(MSG_abort);
128			return 0;
129		}
130		if (md_check_partitions() == 0)
131			goto rdb_edit_check;
132
133		return 1;
134	}
135
136	/*
137	 * Initialize global variables that track space used on this disk.
138	 * Standard 4.4BSD 8-partition labels always cover whole disk.
139	 */
140	if (ptsize == 0)
141		ptsize = dlsize - ptstart;
142	if (dlsize == 0)
143		dlsize = ptstart + ptsize;
144
145	partstart = ptstart;
146	ptend = ptstart + ptsize;
147
148	/* Ask for layout type -- standard or special */
149	msg_display(MSG_layout,
150		    ptsize / (MEG / sectorsize),
151		    DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE,
152		    DEFROOTSIZE + DEFSWAPSIZE + DEFUSRSIZE + XNEEDMB);
153
154	process_menu(MENU_layout, NULL);
155
156	/* Set so we use the 'real' geometry for rounding, input in MB */
157	current_cylsize = dlcylsize;
158	set_sizemultname_meg();
159
160	/* Build standard partitions */
161	memset(&bsdlabel, 0, sizeof bsdlabel);
162
163	/* Set initial partition types to unused */
164	for (part = 0 ; part < maxpart ; ++part)
165		bsdlabel[part].pi_fstype = FS_UNUSED;
166
167	/* Whole disk partition */
168	part_raw = getrawpartition();
169	if (part_raw == -1)
170		part_raw = PART_C;	/* for sanity... */
171	bsdlabel[part_raw].pi_offset = 0;
172	bsdlabel[part_raw].pi_size = dlsize;
173
174	if (part_raw == PART_D) {
175		/* Probably a system that expects an i386 style mbr */
176		part_bsd = PART_C;
177		bsdlabel[PART_C].pi_offset = ptstart;
178		bsdlabel[PART_C].pi_size = ptsize;
179	} else {
180		part_bsd = part_raw;
181	}
182
183	if (bootsize != 0) {
184		bsdlabel[PART_BOOT_FAT12].pi_fstype = FS_MSDOS;
185		bsdlabel[PART_BOOT_FAT12].pi_size = bootsize;
186		bsdlabel[PART_BOOT_FAT12].pi_offset = bootstart;
187		bsdlabel[PART_BOOT_FAT12].pi_flags |= PART_BOOT_FAT12_PI_FLAGS;
188		strlcpy(bsdlabel[PART_BOOT_FAT12].pi_mount,
189		    PART_BOOT_FAT12_PI_MOUNT,
190		    sizeof bsdlabel[PART_BOOT_FAT12].pi_mount);
191	}
192	if (binfosize != 0) {
193		bsdlabel[PART_BOOT_BINFO].pi_fstype = FS_OTHER;
194		bsdlabel[PART_BOOT_BINFO].pi_size = binfosize;
195		bsdlabel[PART_BOOT_BINFO].pi_offset = binfostart;
196	}
197	if (bprepsize != 0) {
198		bsdlabel[PART_BOOT_PREP].pi_fstype = FS_BOOT;
199		bsdlabel[PART_BOOT_PREP].pi_size = bprepsize;
200		bsdlabel[PART_BOOT_PREP].pi_offset = bprepstart;
201	}
202
203#ifdef PART_REST
204	bsdlabel[PART_REST].pi_offset = 0;
205	bsdlabel[PART_REST].pi_size = ptstart;
206#endif
207
208	/*
209	 * Save any partitions that are outside the area we are
210	 * going to use.
211	 * In particular this saves details of the other MBR
212	 * partitions on a multiboot i386 system.
213	 */
214	 for (i = maxpart; i--;) {
215		if (bsdlabel[i].pi_size != 0)
216			/* Don't overwrite special partitions */
217			continue;
218		p = &oldlabel[i];
219		if (p->pi_fstype == FS_UNUSED || p->pi_size == 0)
220			continue;
221		if (layoutkind == 4) {
222			if (PI_ISBSDFS(p))
223				p->pi_flags |= PIF_MOUNT;
224		} else {
225			if (p->pi_offset < ptstart + ptsize &&
226			    p->pi_offset + p->pi_size > ptstart)
227				/* Not outside area we are allocating */
228				continue;
229			if (p->pi_fstype == FS_SWAP)
230				no_swap = 1;
231		}
232		bsdlabel[i] = oldlabel[i];
233	 }
234
235	if (layoutkind == 4) {
236		/* XXX Check we have a sensible layout */
237		;
238	} else
239		get_ptn_sizes(partstart, ptend - partstart, no_swap);
240
241	/*
242	 * OK, we have a partition table. Give the user the chance to
243	 * edit it and verify it's OK, or abort altogether.
244	 */
245 edit_check:
246	if (edit_and_check_label(bsdlabel, maxpart, part_raw, part_bsd) == 0) {
247		msg_display(MSG_abort);
248		return 0;
249	}
250	if (md_check_partitions() == 0)
251		goto edit_check;
252
253	/* Disk name */
254	msg_prompt(MSG_packname, bsddiskname, bsddiskname, sizeof bsddiskname);
255
256	/* save label to disk for MI code to update. */
257	(void) savenewlabel(bsdlabel, maxpart);
258
259	/* Everything looks OK. */
260	return 1;
261}
262
263/*
264 * any additional partition validation
265 */
266int
267md_check_partitions(void)
268{
269	int part, fprep=0, ffat=0;
270
271	if (rdb_found)
272		return 1;
273
274	/* we need to find a boot partition, otherwise we can't create
275	 * our msdos fs boot partition.  We make the assumption that
276	 * the user hasn't done something stupid, like move it away
277	 * from the MBR partition.
278	 */
279	for (part = PART_A; part < MAXPARTITIONS; part++) {
280		if (bsdlabel[part].pi_fstype == FS_MSDOS) {
281			bootpart_fat12 = part;
282			ffat++;
283		} else if (bsdlabel[part].pi_fstype == FS_BOOT) {
284			bootpart_prep = part;
285			fprep++;
286		} else if (bsdlabel[part].pi_fstype == FS_OTHER) {
287			bootpart_binfo = part;
288			fprep++;
289		}
290	}
291	/* oh, the confusion */
292	if (ffat >= 1 && fprep < 2)
293		return 1;
294	if (ffat < 1 && fprep >= 2)
295		return 2;
296	if (ffat >=1 && fprep >= 2)
297		return 3;
298
299	msg_display(MSG_nobootpartdisklabel);
300	process_menu(MENU_ok, NULL);
301	return 0;
302}
303
304/*
305 * hook called before writing new disklabel.
306 */
307int
308md_pre_disklabel(void)
309{
310
311	if (rdb_found)
312		return 0;
313
314	msg_display(MSG_dofdisk);
315
316	/* write edited MBR onto disk. */
317	if (write_mbr(diskdev, &mbr, 1) != 0) {
318		msg_display(MSG_wmbrfail);
319		process_menu(MENU_ok, NULL);
320		return 1;
321	}
322	return 0;
323}
324
325/*
326 * hook called after writing disklabel to new target disk.
327 */
328int
329md_post_disklabel(void)
330{
331	char bootdev[100];
332
333	if (bootstart == 0 || bootsize == 0 || rdb_found)
334		return 0;
335
336	snprintf(bootdev, sizeof bootdev, "/dev/r%s%c", diskdev,
337	    'a'+bootpart_fat12);
338	run_program(RUN_DISPLAY, "/sbin/newfs_msdos %s", bootdev);
339
340	return 0;
341}
342
343/*
344 * hook called after upgrade() or install() has finished setting
345 * up the target disk but immediately before the user is given the
346 * ``disks are now set up'' message.
347 */
348int
349md_post_newfs(void)
350{
351
352	/* No bootblock. We use ofwboot from a partition visiable by OFW. */
353	return 0;
354}
355
356int
357md_post_extract(void)
358{
359	char bootdev[100], bootbdev[100], version[64];
360
361	/* if we can't make it bootable, just punt */
362	if ((nobootfix && noprepfix) || rdb_found)
363		return 0;
364
365	snprintf(version, sizeof version, "NetBSD/%s %s", MACH, REL);
366	run_program(RUN_DISPLAY, "/usr/mdec/mkbootinfo '%s' %d "
367	    "/tmp/bootinfo.txt", version, bootinfo_mbr);
368
369	if (!nobootfix) {
370		run_program(RUN_DISPLAY, "/bin/mkdir -p /%s/boot/ppc",
371		    target_prefix());
372		run_program(RUN_DISPLAY, "/bin/mkdir -p /%s/boot/netbsd",
373		    target_prefix());
374		run_program(RUN_DISPLAY, "/bin/cp /usr/mdec/ofwboot "
375		    "/%s/boot/netbsd", target_prefix());
376		run_program(RUN_DISPLAY, "/bin/cp /tmp/bootinfo.txt "
377		    "/%s/boot/ppc", target_prefix());
378		run_program(RUN_DISPLAY, "/bin/cp /usr/mdec/ofwboot "
379		    "/%s/boot/ofwboot", target_prefix());
380	}
381
382	if (!noprepfix) {
383		snprintf(bootdev, sizeof bootdev, "/dev/r%s%c", diskdev,
384		    'a'+bootpart_prep);
385		snprintf(bootbdev, sizeof bootbdev, "/dev/%s%c", diskdev,
386		    'a'+bootpart_prep);
387		run_program(RUN_DISPLAY, "/bin/dd if=/dev/zero of=%s bs=512",
388		    bootdev);
389		run_program(RUN_DISPLAY, "/bin/dd if=/usr/mdec/ofwboot "
390		    "of=%s bs=512", bootbdev);
391
392		snprintf(bootdev, sizeof bootdev, "/dev/r%s%c", diskdev,
393		    'a'+bootpart_binfo);
394		snprintf(bootbdev, sizeof bootbdev, "/dev/%s%c", diskdev,
395		    'a'+bootpart_binfo);
396		run_program(RUN_DISPLAY, "/bin/dd if=/dev/zero of=%s bs=512",
397		    bootdev);
398		run_program(RUN_DISPLAY, "/bin/dd if=/tmp/bootinfo.txt "
399		    "of=%s bs=512", bootbdev);
400	}
401
402	return 0;
403}
404
405void
406md_cleanup_install(void)
407{
408
409#ifndef DEBUG
410	enable_rc_conf();
411#endif
412}
413
414int
415md_pre_update(void)
416{
417	struct mbr_partition *part;
418	mbr_info_t *ext;
419	int i;
420
421	if (check_rdb())
422		return 1;
423
424	read_mbr(diskdev, &mbr);
425	/* do a sanity check of the partition table */
426	for (ext = &mbr; ext; ext = ext->extended) {
427		part = ext->mbr.mbr_parts;
428		for (i = 0; i < MBR_PART_COUNT; part++, i++) {
429			if (part->mbrp_type == MBR_PTYPE_PREP &&
430			    part->mbrp_size > 50)
431				bootinfo_mbr = i+1;
432			if (part->mbrp_type == MBR_PTYPE_RESERVED_x21 &&
433			    part->mbrp_size < (MIN_FAT12_BOOT/512)) {
434				msg_display(MSG_boottoosmall);
435				msg_display_add(MSG_nobootpartdisklabel, 0);
436				process_menu(MENU_yesno, NULL);
437				if (!yesno)
438					return 0;
439				nobootfix = 1;
440			}
441		}
442	}
443
444	i = md_check_partitions();
445	switch (i) {
446	case 0: nobootfix=1; noprepfix=1; break;
447	case 1: noprepfix=1; break;
448	case 2: nobootfix=1; break;
449	default: break;
450	}
451
452	return 1;
453}
454
455/* Upgrade support */
456int
457md_update(void)
458{
459
460	nonewfsmsdos = 1;
461	md_post_newfs();
462	return 1;
463}
464
465
466int
467md_check_mbr(mbr_info_t *mbri)
468{
469	mbr_info_t *ext;
470	struct mbr_partition *part;
471	int i;
472
473	for (ext = mbri; ext; ext = ext->extended) {
474		part = ext->mbr.mbr_parts;
475		for (i = 0; i < MBR_PART_COUNT; part++, i++) {
476			if (part->mbrp_type == MBR_PTYPE_FAT12) {
477				bootstart = part->mbrp_start;
478				bootsize = part->mbrp_size;
479			} else if (part->mbrp_type == MBR_PTYPE_PREP &&
480			    part->mbrp_size < 50) {
481				/* this is the bootinfo partition */
482				binfostart = part->mbrp_start;
483				binfosize = part->mbrp_size;
484				bootinfo_mbr = i+1;
485			} else if (part->mbrp_type == MBR_PTYPE_PREP &&
486			    part->mbrp_size > 50) {
487				bprepstart = part->mbrp_start;
488				bprepsize = part->mbrp_size;
489			}
490			break;
491		}
492	}
493
494	/* we need to either have a pair of prep partitions, or a single
495	 * fat.  if neither, things are broken. */
496	if (!(bootsize >= (MIN_FAT12_BOOT/512) ||
497		(binfosize >= (MIN_BINFO_BOOT/512) &&
498		    bprepsize >= (MIN_PREP_BOOT/512)))) {
499		msg_display(MSG_bootnotright);
500		msg_display_add(MSG_reeditpart, 0);
501		process_menu(MENU_yesno, NULL);
502		if (!yesno)
503			return 0;
504		return 1;
505	}
506
507	/* check the prep partitions */
508	if ((binfosize > 0 || bprepsize > 0) &&
509	    (binfosize < (MIN_BINFO_BOOT/512) ||
510		bprepsize < (MIN_PREP_BOOT/512))) {
511		msg_display(MSG_preptoosmall);
512		msg_display_add(MSG_reeditpart, 0);
513		process_menu(MENU_yesno, NULL);
514		if (!yesno)
515			return 0;
516		return 1;
517	}
518
519	/* check the fat12 parititons */
520	if (bootsize > 0 && bootsize < (MIN_FAT12_BOOT/512)) {
521		msg_display(MSG_boottoosmall);
522		msg_display_add(MSG_reeditpart, 0);
523		process_menu(MENU_yesno, NULL);
524		if (!yesno)
525			return 0;
526		return 1;
527	}
528
529	/* if both sets contain zero, thats bad */
530	if ((bootstart == 0 || bootsize == 0) &&
531	    (binfosize == 0 || binfostart == 0 ||
532		bprepsize == 0 || bprepstart == 0)) {
533		msg_display(MSG_nobootpart);
534		msg_display_add(MSG_reeditpart, 0);
535		process_menu(MENU_yesno, NULL);
536		if (!yesno)
537			return 0;
538		return 1;
539	}
540	return 2;
541}
542
543/*
544 * NOTE, we use a reserved partition type, because some RS/6000 machines hang
545 * hard if they find a FAT12, and if we use type prep, that indicates that
546 * it should be read raw.
547 * One partition for FAT12 booting
548 * One partition for NetBSD
549 * One partition to hold the bootinfo.txt file
550 * One partition to hold ofwboot
551 */
552
553int
554md_mbr_use_wholedisk(mbr_info_t *mbri)
555{
556	struct mbr_sector *mbrs = &mbri->mbr;
557	mbr_info_t *ext;
558	struct mbr_partition *part;
559
560	part = &mbrs->mbr_parts[0];
561	/* Set the partition information for full disk usage. */
562	while ((ext = mbri->extended)) {
563		mbri->extended = ext->extended;
564		free(ext);
565	}
566	memset(part, 0, MBR_PART_COUNT * sizeof *part);
567
568	part[0].mbrp_type = MBR_PTYPE_RESERVED_x21;
569	part[0].mbrp_size = FAT12_BOOT_SIZE/512;
570	part[0].mbrp_start = bsec;
571	part[0].mbrp_flag = 0;
572
573	part[1].mbrp_type = MBR_PTYPE_NETBSD;
574	part[1].mbrp_size = dlsize - (bsec + FAT12_BOOT_SIZE/512 +
575	    BINFO_BOOT_SIZE/512 + PREP_BOOT_SIZE/512);
576	part[1].mbrp_start = bsec + FAT12_BOOT_SIZE/512 + BINFO_BOOT_SIZE/512 +
577	    PREP_BOOT_SIZE/512;
578	part[1].mbrp_flag = MBR_PFLAG_ACTIVE;
579
580	part[2].mbrp_type = MBR_PTYPE_PREP;
581	part[2].mbrp_size = BINFO_BOOT_SIZE/512;
582	part[2].mbrp_start = bsec + FAT12_BOOT_SIZE/512;
583	part[2].mbrp_flag = 0;
584
585	part[3].mbrp_type = MBR_PTYPE_PREP;
586	part[3].mbrp_size = PREP_BOOT_SIZE/512;
587	part[3].mbrp_start = bsec + FAT12_BOOT_SIZE/512 + BINFO_BOOT_SIZE/512;
588	part[3].mbrp_flag = 0;
589
590	ptstart = part[1].mbrp_start;
591	ptsize = part[1].mbrp_size;
592	bootstart = part[0].mbrp_start;
593	bootsize = part[0].mbrp_size;
594	binfostart = part[2].mbrp_start;
595	binfosize= part[2].mbrp_size;
596	bprepstart = part[3].mbrp_start;
597	bprepsize = part[3].mbrp_size;
598	bootinfo_mbr = 4;
599
600	return 1;
601}
602
603const char *md_disklabel_cmd(void)
604{
605
606	/* we cannot rewrite an RDB disklabel */
607	if (rdb_found)
608		return "sync No disklabel";
609
610	return "disklabel -w -r";
611}
612
613static int
614check_rdb(void)
615{
616	char buf[512], diskpath[MAXPATHLEN];
617	struct rdblock *rdb;
618	off_t blk;
619	int fd;
620
621	/* Find out if this disk has a valid RDB, before continuing. */
622	rdb = (struct rdblock *)buf;
623	fd = opendisk(diskdev, O_RDONLY, diskpath, sizeof(diskpath), 0);
624	if (fd < 0)
625		return 0;
626	for (blk = 0; blk < RDB_MAXBLOCKS; blk++) {
627		if (pread(fd, rdb, 512, blk * 512) != 512)
628			return 0;
629		if (rdb->id == RDBLOCK_ID && rdbchksum(rdb) == 0) {
630			rdb_found = 1;	/* do not repartition! */
631			return 1;
632		}
633	}
634	return 0;
635}
636
637static uint32_t
638rdbchksum(void *bdata)
639{
640	uint32_t *blp, cnt, val;
641
642	blp = bdata;
643	cnt = blp[1];
644	val = 0;
645	while (cnt--)
646		val += *blp++;
647	return val;
648}
649
650int
651md_pre_mount()
652{
653
654	return 0;
655}
656