/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */
#ident "$Header: dumpconf.c 1.7 90/07/31 $"

#include <nlist.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/autoconf.h>

#define dprintf if (debug) (void)printf
char *arg0;
int debug = 0;

char *malloc();
void free(), exit(), perror();
off_t lseek();

struct driver_module *driver;
struct gen_device *device;
struct bus *bus;
struct bus *busses;
struct pseudo_dev *pseudo;
char **bus_type;
int *rootbusp;

struct nlist nl[] = {
	{ "busses", },
	{ "drivers", },
	{ "pseudo_dev", },
	{ "bus_type", },
	{ "rootbus", },
	{ 0, },
};

#define PRT_ALL		0x0f
#define PRT_DRIVER	0x01
#define PRT_PSEUDO	0x02
#define PRT_SPACES	0x10
#define PRT_KERNEL	0x20
int	display = 0;
char	*core_file = "/dev/kmem";
char	*unix_name = "/unix";

#define kbusses ((struct bus *)nl[0].n_value)
#define kdrivers ((struct driver_module *)nl[1].n_value)
#define kpseudo_dev ((struct pseudo_dev *)nl[2].n_value)
#define kbus_type ((char **)nl[3].n_value)
#define krootbusp ((int *)nl[4].n_value)

void pexit(str, code)
	char *str;
{
	(void)fprintf( stderr, "%s:  ", arg0 );
	perror( str );
	exit( code );
	/*NOTREACHED*/
}

/*
 * Given a kernel address, a count and type, return a local pointer to the
 * array thus specified
 */
#define knget(ad,cn,type) \
	((type *)kmemget((char *)(ad), (unsigned)((cn)*(sizeof (type)))))

/*
 * kget -- given a kernel address and type, returns a local pointer to the 
 * kernel object.  NOT mmapped.
 */
#define kget(ad,type) knget(ad,1,type)

/*
 * Given a kernel address, an index and type, returns a local pointer to the
 * element of the kernel array.
 */
#define kaget(base,index,type) kget(((type *)(base))+(index), type)

char *kmemget( kp, size)
	char *kp;
	unsigned size;
{
	char *ptr;
	static int kmem = -1;
	
	if (kmem == -1) {
		kmem = open( core_file, O_RDONLY);
		if ( kmem < 0 )
			pexit( "open of core file (kmem)", 1);
	}

	ptr = malloc(size);
	if ( !ptr )
		pexit( "malloc", 2);

	if ( lseek( kmem, (off_t)kp, 0) < 0 )
		pexit( "lseek on kmem", 3);

	if ( read( kmem, ptr, size ) != size )
		pexit( "read on kmem", 4);

	return ptr;
}

void kmemfree( ptr)
	char *ptr;
{
	free( ptr);
}

#define kfree(ptr) kmemfree((char *)(ptr))

char *
ksget( kptr)
	char *kptr;
{
	char *byte;
	int i = 0;

	for (i=0, byte=kget(kptr, char); *byte; i++, byte=kaget(kptr, i, char))
		kfree(byte);

	return knget(kptr, i+1, char);
}

/*ARGSUSED*/
main(argc, argv)
	char *argv[];
{
	extern char	*optarg;
	extern int	optind;
	int	o;

	arg0 = argv[0];

	while ((o = getopt(argc, argv, "ac:dkpsu:")) != EOF) {
		switch(o) {
		case 'a':	/* all, default */
			display |= PRT_ALL;
			break;
		case 'c':	/* core file */
			core_file = optarg;
			break;
		case 'd':	/* drivers */
			display |= PRT_DRIVER;
			break;
		case 'k':	/* kernel config format */
			display |= PRT_KERNEL;
			break;
		case 'p':	/* pseudos */
			display |= PRT_PSEUDO;
			break;
		case 's':	/* space definitions */
			display |= PRT_SPACES;
			break;
		case 'u':	/* /unix name */
			unix_name = optarg;
			break;
		case '?':
		default:
			(void)fprintf(stderr,
				"usage: %s [-adkps] [-c core] [-u unix]\n", arg0);
			exit(1);
		}
	}
	if (!(display & PRT_ALL))
		display |= PRT_ALL;

	/* First, find busses, drivers, pseudo_dev and bus_type tables. */
	if (nlist(unix_name, nl))
		pexit( "nlist:", 1);

	if (display & PRT_DRIVER)
		print_tbl();

	if (display & PRT_PSEUDO)
		print_pseudo();

	dprintf( "# DONE\n");
	return(0);
}

print_tbl()
{
	int busmaxtype = 0, i, j, k, rootbus;
	long *busnumbytype, *busmaxtypes;
	int busnum, unit;

	dprintf("# busses at 0x%x, drivers at 0x%x, pseudo_dev at 0x%x, ",
		(long)kbusses, (long)kdrivers, (long)kpseudo_dev);
	dprintf("# bus_type at 0x%x, rootbus @ 0x%x\n",  kbus_type, krootbusp);
	
	rootbusp = kget( krootbusp, int);
	rootbus = *rootbusp;
	kfree( rootbusp );

	/* Find number of busses and maximum bus type */
	for ( i=0, bus=kget(kbusses, struct bus);
		bus->bustype;
		i++, kfree(bus), bus=kaget(kbusses, i, struct bus)) {
		dprintf( "# reading bus[%i] of type %d\n", i, bus->bustype);
		if ( bus->bustype > busmaxtype )
			busmaxtype = bus->bustype;
	}
	kfree(bus);

	/* read the busses array in one block */
	dprintf( "# reading busses (%d records + sentinel)\n", i);
	busses = knget( kbusses, i+1, struct bus);

	/* Compute array to convert bus number to number within type */
	busnumbytype = (long *)malloc( i * sizeof (long) );
	busmaxtypes = (long *)malloc( (busmaxtype+1) * sizeof (long) );
	if ( ! busnumbytype || ! busmaxtypes )
		pexit( "malloc", 5);
	dprintf( "# reading bus_type\n");
	bus_type = knget( kbus_type, busmaxtype+1, char *);
	for ( i = 0; i < busmaxtype+1; i++ ) {
		bus_type[i] = ksget( bus_type[i]);
		busmaxtypes[i] = 0;
	}

	dprintf( "# computing busnumbytype, max type = %d\n", busmaxtype);
	for ( bus = busses, i = 0; bus->bustype; bus++, i++ ) {
		dprintf("# bus[%d] @ 0x%x, bustype = %d is %s %d\n", i, bus, 
				bus->bustype, bus_type[bus->bustype],
				busmaxtypes[bus->bustype]);
		busnumbytype[i] = busmaxtypes[bus->bustype];
		busmaxtypes[bus->bustype] += 1;
	}

	/* For each driver */
	for ( i=0, driver=kget( kdrivers, struct driver_module);
	     driver->numdev;
	     i++, kfree(driver),
	     driver=kaget(kdrivers, i, struct driver_module) ) {

		driver->name = ksget( driver->name );
		(void)printf( "# %s devices %d found %d configured\n",
		       driver->name, driver->nalive, driver->numdev);
		/* for each device */
		for ( j=0, device=kget( driver->devices, struct gen_device);
		     j < driver->numdev;
		     j++, kfree(device),
		     device=kget( (char *)driver->devices+j*driver->size_dev,
					struct gen_device) )
		{
			if (!device->driver)
				continue;
			dprintf("# %s%d probed alive\n", driver->name, j);

			/* Device is alive, so announce it */
			busnum = device->bus - kbusses;
			device->on_bus = kget( device->on_bus,
				struct dev_on_bus);
			unit =  (device->config - device->on_bus->config_array)
				/device->on_bus->nspaces;
			dprintf("# device->bus = 0x%x, busnum = %d\n",
				device->bus, busnum);
			device->config = kget( (char *)device->config,
				struct device_config);
			if (busses[busnum].bustype == rootbus)
			{
				(void)printf("%s %d\ton %s\t", driver->name, 
					j,
			       		bus_type[busses[busnum].bustype]);
				(void)printf("unit %d\t# slic %d\n", unit,
					device->config->dev_addr);
			} else {
				(void)printf("%s", driver->name);
				if (!(display & PRT_KERNEL))
					(void)printf(" %d", j);
				(void)printf("\ton %s %d\t",
			       		bus_type[busses[busnum].bustype],
			       		busnumbytype[busnum]);
				(void)printf("unit %d\t", unit);

				if (display & PRT_SPACES) {
					(void)printf("## space %d addr %d ",
						device->config->space,
						device->config->dev_addr);
				}

				if ( device->busnum < 0 )
					(void)printf("# configured on %s ?",
				       		bus_type[ -device->busnum]);
				else	(void)printf("# configured on %s %d",
					       bus_type[busses[device->busnum]
						.bustype],
						busnumbytype[device->busnum]);
				if ( device->unit < 0 )
					(void)printf(" unit ?");
				else	(void)printf(" unit %d", device->unit);

				if (device->n_maps > 0)
					(void)printf( " using %d map%s\n", 
					       device->n_maps,
					       (device->n_maps == 1)? "":"s");
				else	(void)printf("\n");
			}
			kfree (device->config);

			/*  Announce busses that go with this board */
			for ( k=0; busses[k].bustype; k++ )

				if ((char *) busses[k].owner ==
				    (char *) driver->devices + j * driver->size_dev)
					(void)printf("%s %d\ton %s %d unit ?\n",
					       bus_type[busses[k].bustype],
					       busnumbytype[k],
					       driver->name, j);
		kfree(device->on_bus);
		}			  /* end of for each device */
		kfree(device);		  /* free final device */

		kfree(driver->name);
	}				  /* end of for each driver */
	kfree(driver);			  /* free final driver */

	for (i=0; i<busmaxtype; i++)
		kfree(bus_type[i]);
	kfree(bus_type);
	kfree(busses);

	return;
}

print_pseudo()
{
	int	i;

	/* read the pseudo-devices array in one block */
	dprintf( "# reading pseudo devices\n");
	for ( i=0, pseudo=kget( kpseudo_dev, struct pseudo_dev);
		pseudo->pd_name;
		i++, kfree(pseudo),
		pseudo = kaget(kpseudo_dev, i, struct pseudo_dev) ) {
		dprintf("# pseudo found @ 0x%x\n", pseudo);
		pseudo->pd_name = ksget( pseudo->pd_name );
		if ((long)pseudo->pd_flags != -2) {
			(void)printf("pseudo-device %s\t%lu\n",
				pseudo->pd_name, pseudo->pd_flags);
		} else
			(void)printf("pseudo-device %s\n", pseudo->pd_name);
	}
	kfree(pseudo);			  /* free final pseudo */

	return;
}
