Booting SPARC and x86 Solaris from a single DVD

One of the projects I've been working on at Sun for the past 6 or 7 years is a utility CD that our field service engineers can boot on our customer's systems for the purpose of troubleshooting in a non-intrusive manner. Shortly after Solaris 10 came out, I started toying with the idea of creating a dual boot CD. This would be a Solaris CD that could be booted on both SPARC and x86 systems. I explored this possibility for a while until I came to a technical roadblock. There was just no way to make the x86 MBR co-exist with the SPARC VTOC on sector 0 of the CD, so I dropped the idea.

The other day, I started thinking about this idea again and realized that the last time I had tried this was when Solaris x86 was still using the Device Configuration Assistant as the boot loader. This mechanism required there to be an fdisk partition table in place. However, today's Solaris x86 has no such requirement as it uses GRUB as it's boot loader. GRUB (stage2_eltorito to be exact) is perfectly happy to use the ISO9660 structure of the CD to find and load the miniroot. Since ISO9660 doesn't do anything with the first logical cylinder (first 327680 bytes) of the CD, and El Torito doesn't use anything in the first 32K of the CD, it's possible to put arbitrary data in it without breaking anything on the x86 CD/DVD image.

The arbitrary data in this case can be a SPARC VTOC on sector 0. Because of this, it's possible to create a CD or DVD that can boot Solaris on both SPARC and x86. The general idea is:

  • Make a copy of the x86 ISO image
  • Pad this copy with 0's up to the next cylinder (327680 byte) boundary.
  • Append the SPARC ISO image to it.
  • Take the VTOC from the SPARC image and adjust the starting cylinder (dkl_cylno - see /usr/include/sys/dklabel.h) for all the existing partitions (dkl_nblk != 0) by adding the number of cylinders that the x86 image takes up.
  • Recalculate the VTOC checksum
  • Write the new VTOC to sector 0 of the new composite image.

Below is a program that performs all the steps listed above. This program must be compiled and run on a SPARC machine as Solaris x86 systems don't have the necessary header files to deal with a SPARC VTOC.

/*****************************************************************************/
/*                                                                           */
/* mkdual.c - Create a single DVD image from both the SPARC and x86 versions */
/*            of SUE that is capable of booting on both types of hardware    */
/*                                                                           */
/*                                                                           */
/* Accomplishing this takes advantage of the fact that there is no partition */
/* structure on the GRUB-based x86 CD/DVD. We can sneak the SPARC VTOC into  */
/* sector 0 on the x86 image since x86 doesn't use this area. The basic      */
/* steps performed by this program are:                                      */
/*                                                                           */
/* - Copy the x86 image                                                      */
/* - Take the size of the x86 image and pad up to the next cylinder boundary */
/* - Read the SPARC image VTOC                                               */
/* - Modify the starting cylinders (dkl_cylno) of the VTOC slices by adding  */
/*   the offset represented by the size of the x86 image + padding           */
/* - Tack on the SPARC image to the copied x86 image.                        */
/* - Calculate the new checksum for the SPARC VTOC                           */
/* - Write the new modified VTOC to sector 0 of the composite image          */
/*                                                                           */
/* This may need to be modified when SPARC newboot hits Solaris 10 (u6)      */
/*                                                                           */
/* J. Cecere                                                                 */
/*                                                                           */
/*****************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/vtoc.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#include <strings.h>

#define ISOCYL 327680           /* Size of a cylinder on an ISO 9660 fs */
#define BUFSIZE 131072          /* Read/write buffer size               */
#define PTYPE "sparc"       /* Type of machine this can be run on   */

struct dk_label get_vtoc(char *dev) {

        /* Read in a VTOC from the given device. Exit if it's not a VTOC */

        int fd,a;
        struct dk_label vt;

        if ((fd=open(dev, O_RDONLY)) == -1) {
                perror(dev);
                exit(1);
        }

        if ((a = read (fd, (void *)&vt, sizeof (struct dk_label))) == -1) {
                perror("read:");
                exit(a);
        }

        close (fd);

        if (vt.dkl_vtoc.v_sanity != VTOC_SANE) {
                fprintf(stderr,"No VTOC found\n");
                exit(1);
        }

        return(vt);
}

int main(int argc, char *argv[]) {

        char *x86image,*sparcimage,*outfile;
        int a,k;
        int ifd,ofd;
        struct dk_label vt;
        unsigned short *ckptr;
        unsigned short cksum;
        char buf[BUFSIZE];
        struct stat ibuf;
        long long total;
        int percent,oldpercent;
        int numcyl,pad;
        char *padbuf;
        char ptype[SYS_NMLN];

        /* First check that we're running on SPARC */

        sysinfo(SI_ARCHITECTURE, ptype, sizeof(ptype));

        if (strcmp(ptype,PTYPE) != 0) {
                fprintf(stderr,"%s must be run on SPARC\n",*argv);
                exit(1);
        }

        /* Usage requires three files as arguments */

        if (argc != 4) {
                printf("Usage: %s <x86 image> <sparc image> <outfile>\n",*argv);
                exit(0);
        }

        x86image   = *(argv+1);
        sparcimage = *(argv+2);
        outfile    = *(argv+3);

        /* Get the VTOC from the SPARC image */

        vt = get_vtoc(sparcimage);

        /* Copy the x86 image */

        if ((ifd=open(x86image, O_RDONLY)) == -1) {
                perror(x86image);
                exit(1);
        }

        /* Get the file size */

        stat(x86image,&ibuf);

        /* Open the output file with the correct mode */

        if ((ofd=open(outfile,O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
                perror("outfile");
                exit(1);
        }

        /* total, percent, and oldpercent are used to print out the */
        /* percentage complete while the copy is being performed    */

        total=0;
        percent=0;
        oldpercent=-1;

        /* Copy the ISO file into the image file */

        while ((a=read(ifd,&buf,sizeof(buf))) != 0) {
                if (a == -1) {
                        perror(x86image);
                        exit(-1);
                }

                total+=a;
                percent=(total*100)/ibuf.st_size;
                if (percent != oldpercent) {
                        printf("rCopying in x86 image...%d%%",percent);
                        fflush(stdout);
                        oldpercent=percent;
                }

                if ((write(ofd,&buf,a)) == -1) {
                        perror(outfile);
                        exit(-1);
                }
        }

        close(ifd);

        printf("rCopying in x86 image...done.\n");

        printf("Calculating new offsets and padding...");

        /* numcyl is the number of cylinders in the x86 image, before figuring
           out if we need padding. */

        numcyl = ibuf.st_size/ISOCYL;

        /* Calculate the number of pad bytes we need to have the end of the x86
           image fall on a cylinder boundary */

        pad = ISOCYL - (ibuf.st_size%ISOCYL);

        /* If our pad is our cylinder size or 0, then we don't need padding as
           our image already falls on a cylinder boundary */

        if (pad != 0 && pad != ISOCYL) {

                /* We need padding. Add one to the number of cylinders since we're
                   padding up. Then create a padding buffer. */

                numcyl++;
                padbuf = malloc(pad);

                /* Ok, so we didn't put anything in padbuf, so whatever garbage
                   happens to be in memory will be in it. However, we don't care
                   since this area of the image will never be used for anything.
                   Write the padding buffer out. */

                write(ofd,padbuf,pad);
                free(padbuf);
        }

        /* Now we take the starting cylinders for all our slices and add the
           offset of the size of the x86 image (which will be in whole cylinders */

        for (k=0;k<NDKMAP;k++) {

                /* If this slice wasn't defined, don't bother */

                if (vt.dkl_map[k].dkl_nblk != 0) {

                        /* Recalculate the cylinder offsets for the slices. */

                        vt.dkl_map[k].dkl_cylno += numcyl;

                }
        }
        printf("done\n");

        printf("Calculating new VTOC checksum...");
        cksum = 0;
        ckptr = (unsigned short *)&vt;
        for (k=0; k<255; k++) {
                cksum ^= *ckptr;
                ckptr++;
        }

        vt.dkl_cksum = cksum;
        printf("done\n");

        /* Get the size of the SPARC image so that we can print an accurate
           percentage count while we're copying it. */

        stat(sparcimage,&ibuf);

        if ((ifd=open(sparcimage, O_RDONLY)) == -1) {
                perror(sparcimage);
                exit(1);
        }

        /* ofd is still open and pointing to the end of the outfile. Start
           appending the SPARC image to it */

        total=0;
        percent=0;
        oldpercent=-1;

        /* Copy the ISO file into the image file */

        while ((a=read(ifd,&buf,sizeof(buf))) != 0) {
                if (a == -1) {
                        perror(sparcimage);
                        exit(-1);
                }

                total+=a;
                percent=(total*100)/ibuf.st_size;
                if (percent != oldpercent) {
                        printf("rAppending SPARC image...%d%%",percent);
                        fflush(stdout);
                        oldpercent=percent;
                }

                if ((write(ofd,&buf,a)) == -1) {
                        perror(outfile);
                        exit(-1);
                }
        }

        close(ifd);
        printf("rAppending SPARC image...done.\n");

        /* Now rewrite the VTOC with the updated information */

        printf("Writing new VTOC...");
        lseek(ofd,0,SEEK_SET);
        write(ofd, (void *)&vt, sizeof(struct dk_label));
        close(ofd);
        printf("done\n");

}





Comments:

Fantastic and thank U

Posted by Sandra Tucker on July 08, 2009 at 06:57 AM EDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

user12609480

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today