[BACK]Return to sgivol.c CVS log [TXT][DIR] Up to [local] / sys / arch / sgi / stand / sgivol

File: [local] / sys / arch / sgi / stand / sgivol / sgivol.c (download)

Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:07:49 2008 UTC (16 years, 3 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD
Changes since 1.1: +0 -0 lines

Import of OpenBSD 4.2 release kernel tree with initial code to support 
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO

Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)

/*	$OpenBSD: sgivol.c,v 1.7 2005/08/01 19:34:50 jmc Exp $	*/
/*	$NetBSD: sgivol.c,v 1.8 2003/11/08 04:59:00 sekiya Exp $	*/

/*-
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Michael Hitch and Hubert Feyrer.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *        This product includes software developed by the NetBSD
 *        Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util.h>

#include <sys/disklabel.h>
#include <sys/endian.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>

/*
 * Some IRIX man pages refer to the size being a multiple of whole cylinders.
 * Later ones only refer to the size being "typically" 2MB.  IRIX fx(1)
 * uses a default drive geometry if one can't be determined, suggesting
 * that "whole cylinder" multiples are not required.
 */

#define SGI_SIZE_VOLHDR	3135	/* Can be overridden via -h parameter */

struct local_devparms {
	u_int8_t	dp_skew;
	u_int8_t	dp_gap1;
	u_int8_t	dp_gap2;
	u_int8_t	dp_spares_cyl;
	u_int16_t	dp_cyls;
	u_int16_t	dp_shd0;
	u_int16_t	dp_trks0;
	u_int8_t	dp_ctq_depth;
	u_int8_t	dp_cylshi;
	u_int16_t	dp_unused;
	u_int16_t	dp_secs;
	u_int16_t	dp_secbytes;
	u_int16_t	dp_interleave;
	u_int32_t	dp_flags;
	u_int32_t	dp_datarate;
	u_int32_t	dp_nretries;
	u_int32_t	dp_mspw;
	u_int16_t	dp_xgap1;
	u_int16_t	dp_xsync;
	u_int16_t	dp_xrdly;
	u_int16_t	dp_xgap2;
	u_int16_t	dp_xrgate;
	u_int16_t	dp_xwcont;
} __packed;

#define	SGI_SIZE_VOLDIR	15

struct local_sgilabel {
#define SGILABEL_MAGIC  0xbe5a941
	u_int32_t	magic;
	int16_t		root;
	int16_t		swap;
	char		bootfile[16];
	struct local_devparms dp;
	struct {
		char		name[8];
		int32_t		block;
		int32_t		bytes;
	} voldir[SGI_SIZE_VOLDIR];
	struct {
		int32_t		blocks;
		int32_t		first;
		int32_t		type;
	} partitions[MAXPARTITIONS];
	int32_t		checksum;
	int32_t		_pad;
} __packed;

#define SGI_PTYPE_VOLHDR	0
#define SGI_PTYPE_RAW		3
#define SGI_PTYPE_BSD		4
#define SGI_PTYPE_VOLUME	6
#define SGI_PTYPE_EFS		7
#define SGI_PTYPE_LVOL		8
#define SGI_PTYPE_RLVOL		9
#define SGI_PTYPE_XFS		10
#define SGI_PTYPE_XFSLOG	11
#define SGI_PTYPE_XLV		12
#define SGI_PTYPE_XVM		13

int	fd;
int	opt_i;			/* Initialize volume header */
int	opt_r;			/* Read a file from volume header */
int	opt_w;			/* Write a file to volume header */
int	opt_l;			/* Link a file in volume header */
int	opt_d;			/* Delete a file from volume header */
int	opt_p;			/* Modify a partition */
int	opt_q;			/* quiet mode */
int	opt_f;			/* Don't ask, just do what you're told */
int	partno, partfirst, partblocks, parttype;
struct local_sgilabel *volhdr;
int32_t	checksum;
u_int32_t	volhdr_size = SGI_SIZE_VOLHDR;

const char *vfilename = "";
const char *ufilename = "";

struct disklabel lbl;

unsigned char buf[DEV_BSIZE];

const char *sgi_types[] = {
	"Volume Header",
	"Repl Trks",
	"Repl Secs",
	"Raw",
	"BSD4.2",
	"SysV",
	"Volume",
	"EFS",
	"LVol",
	"RLVol",
	"XFS",
	"XSFLog",
	"XLV",
	"XVM"
};

void	display_vol(void);
void	init_volhdr(void);
void	read_file(void);
void	write_file(void);
void	link_file(void);
void	delete_file(void);
void	modify_partition(void);
void	write_volhdr(void);
int	allocate_space(int);
void	checksum_vol(void);
void	usage(void);

int
main(int argc, char *argv[])
{
	int ch, oflags;
	char *endp;

	while ((ch = getopt(argc, argv, "irwlpdqfh:")) != -1) {
		switch (ch) {
		/* -i, -r, -w, -l, -d and -p override each other */
		/* -q implies -f */
		case 'q':
			++opt_q;
			++opt_f;
			break;
		case 'f':
			++opt_f;
			break;
		case 'i':
			++opt_i;
			opt_r = opt_w = opt_l = opt_d = opt_p = 0;
			break;
		case 'h':
			volhdr_size = strtol(optarg, &endp, 0);
			if (*endp != '\0' || errno != 0)
				errx(1, "incorrect volume header size: %s",
				    optarg);
			break;
		case 'r':
			++opt_r;
			opt_i = opt_w = opt_l = opt_d = opt_p = 0;
			break;
		case 'w':
			++opt_w;
			opt_i = opt_r = opt_l = opt_d = opt_p = 0;
			break;
		case 'l':
			++opt_l;
			opt_i = opt_r = opt_w = opt_d = opt_p = 0;
			break;
		case 'd':
			++opt_d;
			opt_i = opt_r = opt_w = opt_l = opt_p = 0;
			break;
		case 'p':
			++opt_p;
			opt_i = opt_r = opt_w = opt_l = opt_d = 0;
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (opt_r || opt_w || opt_l) {
		if (argc != 3)
			usage();
		vfilename = argv[0];
		ufilename = argv[1];
		argc -= 2;
		argv += 2;
	}
	if (opt_d) {
		if (argc != 2)
			usage();
		vfilename = argv[0];
		argc--;
		argv++;
	}

	if (opt_p) {
		if (argc != 5)
			usage();
		partno = strtol(argv[0], &endp, 0);
		if (*endp != '\0' || errno != 0 ||
		    partno < 0 || partno > SGI_SIZE_VOLDIR)
			errx(1, "invalid partition number: %s", argv[0]);
		partfirst = strtol(argv[1], &endp, 0);
		if (*endp != '\0' || errno != 0)
			errx(1, "invalid partition start: %s", argv[1]);
		partblocks = strtol(argv[2], &endp, 0);
		if (*endp != '\0' || errno != 0)
			errx(1, "invalid partition size: %s", argv[2]);
		parttype = strtol(argv[3], &endp, 0);
		if (*endp != '\0' || errno != 0)
			errx(1, "invalid partition type: %s", argv[3]);
		argc -= 4;
		argv += 4;
	}
	if (argc != 1)
		usage();

	oflags = ((opt_i | opt_w | opt_l | opt_d | opt_p) ? O_RDWR : O_RDONLY);
	if ((fd = open(argv[0], oflags)) < 0) {
		snprintf(buf, sizeof(buf), "/dev/r%s%c",
		    argv[0], 'a' + getrawpartition());
		if ((fd = open(buf, oflags)) < 0)
			err(1, "open %s", buf);
	}
	if (read(fd, buf, sizeof(buf)) != sizeof(buf))
		err(1, "read volhdr");
	if (ioctl(fd, DIOCGDINFO, &lbl) == -1)
		err(1, "ioctl DIOCGDINFO");
	volhdr = (struct local_sgilabel *)buf;
	if (opt_i) {
		init_volhdr();
		exit(0);
	}
	if (betoh32(volhdr->magic) != SGILABEL_MAGIC)
		errx(2, "no Volume Header found, magic=%x.  Use -i first.",
		    betoh32(volhdr->magic));
	if (opt_r) {
		read_file();
		exit(0);
	}
	if (opt_w) {
		write_file();
		exit(0);
	}
	if (opt_l) {
		link_file();
		exit(0);
	}
	if (opt_d) {
		delete_file();
		exit(0);
	}
	if (opt_p) {
		modify_partition();
		exit(0);
	}

	if (!opt_q)
		display_vol();

	exit (0);
}

void
display_vol(void)
{
	int32_t *l;
	int i;

	printf("disklabel shows %d sectors\n", lbl.d_secperunit);
	l = (int32_t *)buf;
	checksum = 0;
	for (i = 0; i < sizeof(buf) / sizeof(int32_t); ++i)
		checksum += betoh32(l[i]);
	printf("checksum: %08x%s\n", checksum, checksum == 0 ? "" : " *ERROR*");
	printf("root part: %d\n", betoh32(volhdr->root));
	printf("swap part: %d\n", betoh32(volhdr->swap));
	printf("bootfile: %s\n", volhdr->bootfile);
	/* volhdr->devparams[0..47] */
	printf("\nVolume header files:\n");
	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (volhdr->voldir[i].name[0] != '\0') {
			printf("%-8s offset %4d blocks, length %8d bytes (%d blocks)\n",
			    volhdr->voldir[i].name,
			    betoh32(volhdr->voldir[i].block),
			    betoh32(volhdr->voldir[i].bytes),
			    howmany(betoh32(volhdr->voldir[i].bytes),
			    DEV_BSIZE));
		}
	}
	printf("\nSGI partitions:\n");
	for (i = 0; i < MAXPARTITIONS; ++i) {
		if (betoh32(volhdr->partitions[i].blocks) != 0) {
			printf("%2d:%c blocks %8d first %8d type %2d (%s)\n",
			    i, i + 'a', betoh32(volhdr->partitions[i].blocks),
			    betoh32(volhdr->partitions[i].first),
			    betoh32(volhdr->partitions[i].type),
			    betoh32(volhdr->partitions[i].type) >
			    (sizeof(sgi_types) / sizeof(sgi_types[0])) ?
			    "???" :
			    sgi_types[betoh32(volhdr->partitions[i].type)]);
		}
	}
}

void
init_volhdr(void)
{
	memset(buf, 0, sizeof(buf));
	volhdr->magic = htobe32(SGILABEL_MAGIC);
	volhdr->root = htobe16(0);
	volhdr->swap = htobe16(1);
	strlcpy(volhdr->bootfile, "/bsd", sizeof(volhdr->bootfile));
	volhdr->dp.dp_skew = lbl.d_trackskew;
	volhdr->dp.dp_gap1 = 1; /* XXX */
	volhdr->dp.dp_gap2 = 1; /* XXX */
	volhdr->dp.dp_cyls = htobe16(lbl.d_ncylinders);
	volhdr->dp.dp_shd0 = 0;
	volhdr->dp.dp_trks0 = htobe16(lbl.d_ntracks);
	volhdr->dp.dp_secs = htobe16(lbl.d_nsectors);
	volhdr->dp.dp_secbytes = htobe16(lbl.d_secsize);
	volhdr->dp.dp_interleave = htobe16(lbl.d_interleave);
	volhdr->dp.dp_nretries = htobe32(22);
	volhdr->partitions[10].blocks = htobe32(lbl.d_secperunit);
	volhdr->partitions[10].first = 0;
	volhdr->partitions[10].type = htobe32(SGI_PTYPE_VOLUME);
	volhdr->partitions[8].blocks = htobe32(volhdr_size);
	volhdr->partitions[8].first = 0;
	volhdr->partitions[8].type = htobe32(SGI_PTYPE_VOLHDR);
	volhdr->partitions[0].blocks = htobe32(lbl.d_secperunit - volhdr_size);
	volhdr->partitions[0].first = htobe32(volhdr_size);
	volhdr->partitions[0].type = htobe32(SGI_PTYPE_BSD);
	write_volhdr();
}

void
read_file(void)
{
	FILE *fp;
	int i;

	if (!opt_q)
		printf("Reading file %s\n", vfilename);
	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (strncmp(vfilename, volhdr->voldir[i].name,
		    strlen(volhdr->voldir[i].name)) == 0)
			break;
	}
	if (i >= SGI_SIZE_VOLDIR)
		errx(1, "%s: file not found", vfilename);
	/* XXX assumes volume header starts at 0? */
	lseek(fd, betoh32(volhdr->voldir[i].block) * DEV_BSIZE, SEEK_SET);
	if ((fp = fopen(ufilename, "w")) == NULL)
		err(1, "open %s", ufilename);
	i = betoh32(volhdr->voldir[i].bytes);
	while (i > 0) {
		if (read(fd, buf, sizeof(buf)) != sizeof(buf))
			err(1, "read file");
		fwrite(buf, 1, i > sizeof(buf) ? sizeof(buf) : i, fp);
		i -= i > sizeof(buf) ? sizeof(buf) : i;
	}
	fclose(fp);
}

void
write_file(void)
{
	FILE *fp;
	int slot;
	int block, i;
	struct stat st;
	char fbuf[DEV_BSIZE];

	if (!opt_q)
		printf("Writing file %s\n", ufilename);
	if (stat(ufilename, &st) != 0)
		err(1, "stat %s", ufilename);
	if (st.st_size == 0)
		errx(1, "%s: file is empty", vfilename);
	if (!opt_q)
		printf("File %s has %lld bytes\n", ufilename, st.st_size);
	slot = -1;
	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (volhdr->voldir[i].name[0] == '\0' && slot < 0)
			slot = i;
		if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
			slot = i;
			break;
		}
	}
	if (slot == -1)
		errx(1, "no more directory entries available");
	/* -w can overwrite, -a won't overwrite */
	if (betoh32(volhdr->voldir[slot].block) > 0) {
		if (!opt_q)
			printf("File %s exists, removing old file\n",
			    vfilename);
		volhdr->voldir[slot].name[0] = 0;
		volhdr->voldir[slot].block = volhdr->voldir[slot].bytes = 0;
	}
	/* XXX assumes volume header starts at 0? */
	block = allocate_space((int)st.st_size);
	if (block < 0)
		errx(1, "no more space available");

	/*
	 * Make sure the name in the volume header is max. 8 chars,
	 * NOT including NUL.
	 */
	if (strlen(vfilename) > sizeof(volhdr->voldir[slot].name))
		warnx("%s: filename is too long and will be truncated",
		    vfilename);
	strncpy(volhdr->voldir[slot].name, vfilename,
	    sizeof(volhdr->voldir[slot].name));

	volhdr->voldir[slot].block = htobe32(block);
	volhdr->voldir[slot].bytes = htobe32(st.st_size);

	write_volhdr();

	/* write the file itself */
	if (lseek(fd, block * DEV_BSIZE, SEEK_SET) == -1)
		err(1, "lseek write");
	i = st.st_size;
	fp = fopen(ufilename, "r");
	while (i > 0) {
		fread(fbuf, 1, i > sizeof(fbuf) ? sizeof(fbuf) : i, fp);
		if (write(fd, fbuf, sizeof(fbuf)) != sizeof(fbuf))
			err(1, "write file");
		i -= i > sizeof(fbuf) ? sizeof(fbuf) : i;
	}
}

void
link_file(void)
{
	int slot, i;
	int32_t block, bytes;

	if (!opt_q)
		printf("Linking file %s to %s\n", vfilename, ufilename);
	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (strncmp(vfilename, volhdr->voldir[i].name,
		    strlen(volhdr->voldir[i].name)) == 0)
			break;
	}
	if (i >= SGI_SIZE_VOLDIR)
		errx(1, "%s: file not found", vfilename);

	block = volhdr->voldir[i].block;
	bytes = volhdr->voldir[i].bytes;

	slot = -1;
	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (volhdr->voldir[i].name[0] == '\0' && slot < 0)
			slot = i;
		if (strcmp(ufilename, volhdr->voldir[i].name) == 0) {
			slot = i;
			break;
		}
	}
	if (slot == -1)
		errx(1, "no more directory entries available");

	/*
	 * Make sure the name in the volume header is max. 8 chars,
	 * NOT including NUL.
	 */
	if (strlen(ufilename) > sizeof(volhdr->voldir[slot].name))
		warnx("%s: filename is too long and will be truncated",
		    ufilename);
	strncpy(volhdr->voldir[slot].name, ufilename,
	    sizeof(volhdr->voldir[slot].name));

	volhdr->voldir[slot].block = block;
	volhdr->voldir[slot].bytes = bytes;
	write_volhdr();
}

void
delete_file(void)
{
	int i;

	for (i = 0; i < SGI_SIZE_VOLDIR; ++i) {
		if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
			break;
		}
	}
	if (i >= SGI_SIZE_VOLDIR)
		errx(1, "%s: file not found", vfilename);

	/* XXX: we don't compact the file space, so get fragmentation */
	volhdr->voldir[i].name[0] = '\0';
	volhdr->voldir[i].block = volhdr->voldir[i].bytes = 0;
	write_volhdr();
}

void
modify_partition(void)
{
	if (!opt_q)
		printf("Modify partition %d start %d length %d\n",
		    partno, partfirst, partblocks);
	volhdr->partitions[partno].blocks = htobe32(partblocks);
	volhdr->partitions[partno].first = htobe32(partfirst);
	volhdr->partitions[partno].type = htobe32(parttype);
	write_volhdr();
}

void
write_volhdr(void)
{
	int i;

	checksum_vol();

	if (!opt_q)
		display_vol();
	if (!opt_f) {
		printf("\nDo you want to update volume (y/n)? ");
		i = getchar();
		if (i != 'Y' && i != 'y')
			exit(1);
	}
	if (lseek(fd, 0, SEEK_SET) == -1)
		err(1, "lseek 0");
	if (write(fd, buf, sizeof(buf)) != sizeof(buf))
		err(1, "write volhdr");
}

int
allocate_space(int size)
{
	int n, blocks;
	int first;

	blocks = howmany(size, DEV_BSIZE);
	first = 2;
	for (n = 0; n < SGI_SIZE_VOLDIR;) {
		if (volhdr->voldir[n].name[0]) {
			if (first < (betoh32(volhdr->voldir[n].block) +
			    howmany(betoh32(volhdr->voldir[n].bytes),
			    DEV_BSIZE)) &&
			    (first + blocks) > betoh32(volhdr->voldir[n].block)) {
				first = betoh32(volhdr->voldir[n].block) +
				    howmany(betoh32(volhdr->voldir[n].bytes),
				    DEV_BSIZE);
#if 0
				printf("allocate: n=%d first=%d blocks=%d size=%d\n",
				    n, first, blocks, size);
				printf("%s %d %d\n", volhdr->voldir[n].name,
				    volhdr->voldir[n].block,
				    volhdr->voldir[n].bytes);
				printf("first=%d block=%d last=%d end=%d\n",
				    first, volhdr->voldir[n].block,
				    first + blocks - 1,
				    volhdr->voldir[n].block +
				    howmany(volhdr->voldir[n].bytes, DEV_BSIZE));
#endif
				n = 0;
				continue;
			}
		}
		++n;
	}
	if (first + blocks > lbl.d_secperunit)
		first = -1;
	/* XXX assumes volume header is partition 8 */
	/* XXX assumes volume header starts at 0? */
	if (first + blocks >= betoh32(volhdr->partitions[8].blocks))
		first = -1;
	return (first);
}

void
checksum_vol(void)
{
	int32_t *l;
	int i;

	volhdr->checksum = checksum = 0;
	l = (int32_t *)buf;
	for (i = 0; i < sizeof(buf) / sizeof(int32_t); ++i)
		checksum += betoh32(l[i]);
	volhdr->checksum = htobe32(-checksum);
}

void
usage(void)
{
	extern char *__progname;

	fprintf(stderr,
	    "usage: %s [-fq] [-d vhfilename] disk\n"
	    "       %s [-fiq] [-h vhsize] disk\n"
	    "       %s [-fq] [-l vhfilename1 vhfilename2] disk\n"
	    "       %s [-fq] [-r vhfilename diskfilename] disk\n"
	    "       %s [-fq] [-w vhfilename diskfilename] disk\n",
	    __progname, __progname, __progname, __progname, __progname);
	exit(1);
}