Subject: port-i386/18350: /usr/mdec/installboot cannot install boot loader on RAID 1 devices
To: None <gnats-bugs@gnats.netbsd.org>
From: Wolfgang Stukenbrock <wgstuken@localhost.nagler-company.com>
List: netbsd-bugs
Date: 09/20/2002 17:43:54
>Number:         18350
>Category:       port-i386
>Synopsis:       /usr/mdec/installboot cannot install boot loader on RAID 1 devices
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-i386-maintainer
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Fri Sep 20 07:02:00 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     Wolfgang Stukenbrock
>Release:        NetBSD 1.6
>Organization:
	Dr. Nagler & Comapny GmbH
>Environment:
	not relevant
System: NetBSD 1.6 NetBSD 1.6 (GENERIC) #0: Fri Aug 23 15:15:25 CEST 2002 wgstuken@:/usr/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:
	/usr/mdec/installboot cannot install boot loader on RAID 1 devices.
	This reports carries a fix for this problem, that should be revied by a maintainer and then
	(hopefully) be integrated into the source tree for future releases.
	(Patch information see Fix-section of this report)
>How-To-Repeat:
	not relevant
>Fix:
	The folling lines are the output of "diff -c" that may be applied with patch to the file
	"/usr/src/sys/arch/i386/stand/installboot/installboot.c".
	This Patch will enable installboot to install the boot loader on RAID1 devices.
	It assumes that the boot partitions start at the beginning of the RAID device. This ist
	the same assumtion as in the bootloader itself. (see /usr/src/sys/arch/i386/stand/lib/biosdisk.c)

	Remark: By the way, the comment in biosdisk.c for RF_PROTECTED_SECTORS is wrong. This definition
	comes from raidframevar.h not from rf_optnames.h - and this headerfile should be used instead
	of an explicit #define.

	What does this patch do:
	1. check the boot device for RAID by trying to get the RAID-information.
	   If this fails, act as normal.
	2. it gets the component label from the first device in the RAID and checs for RAID type 1
	3. if that succeds it loops though all of the raid componentens
	   - converts block device name into raw device name and opens it
	   - gets partition offset of the partition
	   - corrects the fraglist with the offset for that component
	   - writes bootloader to partition
	   - resets fraglist to the state before adding the offset
	It prints a WARNING and ignores RAID-components that are not "optimal". So it will only work
	correctly, if all components are up and running. It is possible to repeat the installation of
	the boot loader as often as required. It is nessesary to repeat the installation after
	component replacement.
	At the moment, this patch ignores any spare disks. Perhaps somebody with more knowledge of the
	exact semantics and behavior of the raidframe driver can add support for spare disks.
	If anything fails, it will abort.
	This patch has not been tested against all possible way of installing the boot loader!
	The -b option may case problems, but I don't know how to use it on "normal" disks ...
	"-f" should not be relevant, because there should always be a disklabel on the raid device.

	xxxx start of patch info xxxx

*** installboot.c.orig	Thu Sep 19 16:07:17 2002
--- installboot.c	Fri Sep 20 15:24:23 2002
***************
*** 56,61 ****
--- 56,64 ----
  #include <md5.h>
  #include <sys/ioctl.h>
  
+ #include <dev/raidframe/raidframevar.h>
+ #include <dev/raidframe/raidframeio.h>
+ 
  #include "loadfile.h"
  #include "installboot.h"
  
***************
*** 73,78 ****
--- 76,82 ----
  ino_t save_passthru __P((char *, char *, char *, unsigned int));
  static void usage __P((void));
  int main __P((int, char **));
+ static int write_boot_blk __P((int, char *, size_t));
  
  struct fraglist *fraglist;
  
***************
*** 432,438 ****
  	int devfd = -1;
  	struct disklabel dl;
  	int bsdoffs;
! 	int i, res;
  	int forceifnolabel = 0;
  	char *bootblkname = DEFBBLKNAME;
  	int nowrite = 0;
--- 436,442 ----
  	int devfd = -1;
  	struct disklabel dl;
  	int bsdoffs;
! 	int i;
  	int forceifnolabel = 0;
  	char *bootblkname = DEFBBLKNAME;
  	int nowrite = 0;
***************
*** 557,578 ****
  		fraglist->entries[i].offset += bsdoffs;
  
  	if (!nowrite) {
  		/*
! 	         * write first blocks (max loadsz) to start of BSD partition,
! 	         * skip disklabel (in second disk block)
! 	         */
! 		(void) lseek(devfd, (off_t)0, SEEK_SET);
! 		res = write(devfd, bp, DEV_BSIZE);
! 		if (res < 0) {
! 			warn("final write1");
! 			goto out;
  		}
- 		(void) lseek(devfd, (off_t)(2 * DEV_BSIZE), SEEK_SET);
- 		res = write(devfd, bp + 2 * DEV_BSIZE, size - 2 * DEV_BSIZE);
- 		if (res < 0) {
- 			warn("final write2");
- 			goto out;
- 		}
  	}
  	allok = 1;
  
--- 561,655 ----
  		fraglist->entries[i].offset += bsdoffs;
  
  	if (!nowrite) {
+ 		RF_DeviceConfig_t rf_dev_config;
+ 		RF_ComponentLabel_t rf_comp_lbl;
+ 		void *rf_cfg_ptr, *rf_lbl_ptr;
+ 
+ 		/* check if we have a raid device ... */
+ 		rf_cfg_ptr = &rf_dev_config;
+ 		i = ioctl(devfd, RAIDFRAME_GET_INFO, &rf_cfg_ptr);
+ 		if (i == 0) {
+ 	       /* seems to be a raid device ... */
+ 			if (verbose)
+ 				(void) fprintf(stderr, "found %d raid components ...\n", rf_dev_config.ndevs);
+ 	       /* get component label of first element and check raid level ... */
+ 			rf_lbl_ptr = &rf_comp_lbl;
+ 			(void) memset(&rf_comp_lbl, 0, sizeof(rf_comp_lbl));
+ 			i = ioctl(devfd, RAIDFRAME_GET_COMPONENT_LABEL, &rf_lbl_ptr);
+ 			if (i != 0) {
+ 				warn("failed to get first component label from RAID device");
+ 				goto out;
+ 			}
+ 			if ( rf_comp_lbl.parityConfig != '1' ) {
+ 				warn("RAID level %c not supported for booting",  rf_comp_lbl.parityConfig);
+ 				goto out;
+ 			}
+ 
+ 			for (i=0; i < rf_dev_config.ndevs; i++) {
+ 				if (rf_dev_config.devs[i].status == rf_ds_optimal) {
+ 					int raidfd, l, namelen = strlen(rf_dev_config.devs[i].devname);
+ 					char p, *namebuf;
  		/*
! 		 * change block device name into row device name by adding a 'r' to it
! 		 */
! 					p = rf_dev_config.devs[i].devname[namelen - 1];
! 					namebuf = malloc(namelen + 1);
! 					strcpy(namebuf, rf_dev_config.devs[i].devname);
! 					namebuf[namelen + 1] = '\0';
! 					for (l = namelen; l >= 0; l--) {
! 						if (namebuf[l] == '/') {
! 							namebuf[l + 1] = 'r';
! 							break;
! 						}
! 						namebuf[l + 1] = namebuf[l];
! 					}
! 					if (verbose)
! 						(void) fprintf(stderr,
! 						       "updating RAID-component %d (%s)\n", i, namebuf);
! 					raidfd = open(namebuf, O_RDWR, 0);
! 					if (raidfd < 0) {
! 						warn("open RAID-raw partition RW %s", namebuf);
! 						goto out;
! 					}
! 					if (ioctl(raidfd, DIOCGDINFO, &dl) < 0) {
! 						warn("get disklabel");
! 						close(raidfd);
! 						goto out;
! 					} 
! 					if (!isvalidpart(p) || (p - 'a') >= dl.d_npartitions) {
! 						warnx("invalid partition");
! 						close(raidfd);
! 						goto out;
! 					}
! 	      /*
! 	       * add offset of RAID partition plus RF_PROTECTED_SECTORS to fraglist entries
! 	       */
! 					bsdoffs = dl.d_partitions[p - 'a'].p_offset + RF_PROTECTED_SECTORS;
! 					if (verbose)
! 						(void) fprintf(stderr, "using RAID offset %d\n", bsdoffs);
! 
! 					for (l = 0; l < fraglist->numentries; l++)
! 						fraglist->entries[l].offset += bsdoffs;
! 
! 					if (write_boot_blk(raidfd, bp, size) < 0) {
! 						close(raidfd);
! 						goto out;
! 					}
! 	      /*
! 	       * set fraglist back to previous state ...
! 	       */
! 					for (l = 0; l < fraglist->numentries; l++)
! 						fraglist->entries[l].offset -= bsdoffs;
! 					close(raidfd);
! 					free(namebuf);
! 				} else
! 					(void) fprintf(stderr, "WARNING: RAID-component %d (%s) not optimal - ignored\n",
! 						i, rf_dev_config.devs[i].devname);
! 			      }
! 		} else {
! 			if (write_boot_blk(devfd, bp, size) < 0)
! 				goto out;
  		}
  	}
  	allok = 1;
  
***************
*** 588,590 ****
--- 665,694 ----
  	}
  	return (!allok);
  }
+ 
+ static int write_boot_blk(fd, bp, size)
+ 	int fd;
+ 	char *bp;
+ 	size_t size;
+ {
+ 	int res;
+ /*
+  * write first blocks (max loadsz) to start of BSD partition,
+  * skip disklabel (in second disk block)
+  */
+ 	(void) lseek(fd, (off_t)0, SEEK_SET);
+ 	res = write(fd, bp, DEV_BSIZE);
+ 	if (res < 0) {
+ 		warn("final write1");
+ 		return -1;
+ 	}
+ 	(void) lseek(fd, (off_t)(2 * DEV_BSIZE), SEEK_SET);
+ 	res = write(fd, bp + 2 * DEV_BSIZE, size - 2 * DEV_BSIZE);
+ 	if (res < 0) {
+ 		warn("final write2");
+ 		return -1;
+ 	}
+ 	return 0;
+ }
+ 	
>Release-Note:
>Audit-Trail:
>Unformatted: