Files
Arquivotheca.SunOS-4.1.4/usr.etc/mod/modloadconf.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

918 lines
18 KiB
C

/* @(#)modloadconf.c 1.1 94/10/31 */
/*
* Copyright (c) 1988 by Sun Microsystems, Inc.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <nlist.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sundev/mbvar.h>
#include <sun/autoconf.h>
#include <sun/vddrv.h>
/*
* Process the configuration file. You could get as fancy as you like here.
* The code from the config program could be used to do what we have
* to do here. But for now, the simple table driven approach suffices.
*
* getconfinfo() reads each line of the configuration file and produces
* a linked list of confinfo entries. There is one confinfo entry per
* non-comment line in the configuration file. Then, the list of confinfo
* entries is checked for consistency and if it's ok, an array of
* vddrv entries is produced.
*
* The vddrv array is the actual output of this routine. A pointer to
* the start of the array is then used in the VDLOAD ioctl to load the module.
*
* Here are the types of configuration commands that we support:
*
* 0) # a comment line begins with a "#"
* 1) controller <name><number> at {atmem, atio, obmem} <options>
* 2) device <name><number> at {<controller>, atmem, atio, obmem} <options>
* 3) disk <name><number> at {<controller>, atmem, atio, obmem} <options>
* 4) blockmajor <number>
* 5) charmajor <number>
*
* where <options> are
* drive <drive number>
* csr <csr address>
* irq <interrupt request line>
* priority <interrupt priority>
* dmachan <dma channel>
* flags <flags>
*
* and <controller> on the "device" line means the <name><number> of
* a controller. <name> must not have any numbers in it.
*
* For example:
*
* controller fdc0 at atmem ? csr 0x001000 irq 6 priority 3
* controller fdc2 at atmem ? csr 0x002000 irq 5 priority 2
* disk fd0 at fdc0 drive 0
* #disk fd0 at fdc0 drive 1
* disk fd0 at fdc0 drive 2
* device fd0 at fdc2 drive 0
* disk fd0 at fdc2 drive 1
* blockmajor 51
* charmajor 52
*/
extern char *calloc();
extern long strtol();
extern char *outputfile;
#ifndef sun4c
static void conf_controller(), conf_disk(), conf_device();
#endif /* sun4c */
static void conf_blockmajor(), conf_charmajor(), conf_syscallnum();
#ifndef sun4c
static struct mb_ctlr *gen_ctlr();
static struct mb_device *gen_device();
#ifndef sun386
static struct vec *gen_vector();
#endif
#endif /* sun4c */
static void parse_line(), read_literal();
static struct vdconf *gen_vdconf();
static struct confinfo *getcip();
static char *alloc(), *getword(), *getname(), *getstring();
static int getint();
#ifndef sun4c
static int getnum(), getctlrnum();
#endif /* sun4c */
#ifndef sun4c
static void getdrive(), getcsr(), getirq(), getpriority(), getvector();
static void getdmachan(), getflags(), getbusconn();
#endif /* sun4c */
struct veclist { /* List of VME interrupt vectors */
struct veclist *next;
struct vec vec;
};
/*
* An instance of this structure is malloc'd and filled in for each non-comment
* line in the configuration file. After we've read the entire configuration
* file, we go through this array checking for consistency and then generate
* the vdconf array.
*/
static struct confinfo {
struct confinfo *next; /* pointer to next */
int type; /* type of configuration info */
int linenum; /* linenumber this was gen'd for */
char *name; /* controller or device name */
char *conname; /* name of connection (ctlr or bus) */
int disk; /* conf type is "disk" */
int space; /* bus type */
int drive; /* drive number */
int csr; /* csr address */
int irq; /* interrupt request number */
int priority; /* interrupt priority */
struct veclist *vectors; /* VME interrupt vectors */
int dmachan; /* dma channel */
int flags; /* flags */
int majornum; /* major number */
int syscallnum; /* system call number */
caddr_t data; /* pointer to generic data */
} *confinfo;
/*
* Dispatch table for each type of configuration command.
*/
static struct confdispatch {
char *conftype; /* configuration command */
void (*conffcn)(); /* function to process this type */
} confdispatch[] = {
#ifndef sun4c
{ "controller", conf_controller },
{ "device", conf_device },
{ "disk", conf_disk },
#endif /* sun4c */
{ "blockmajor", conf_blockmajor },
{ "charmajor", conf_charmajor },
{ "syscallnum", conf_syscallnum },
{ NULL, 0 },
};
/*
* Various bus types.
*/
static struct bustype {
char *name;
int type;
} bustype[] = {
#ifdef sun386
{ "obmem", SP_OBMEM },
{ "atmem", SP_ATMEM },
{ "atio", SP_ATIO },
{ NULL, 0 }
#else
{ "obmem", SP_OBMEM },
{ "obio", SP_OBIO },
{ "mbmem", SP_MBMEM },
{ "mbio", SP_MBIO },
{ "vme16d16", SP_VME16D16 },
{ "vme24d16", SP_VME24D16 },
{ "vme32d16", SP_VME32D16 },
{ "vme16d32", SP_VME16D32 },
{ "vme24d32", SP_VME24D32 },
{ "vme32d32", SP_VME32D32 },
{ NULL, 0 }
#endif
};
/*
* Configuration options.
*/
static struct options {
char *name;
void (*optfcn)();
} options[] = {
#ifndef sun4c
{ "drive", getdrive },
{ "csr", getcsr },
{ "vector", getvector },
{ "priority", getpriority },
{ "irq", getirq },
#ifdef sun386
{ "dmachan", getdmachan },
#endif
{ "flags", getflags },
#endif /* sun4c */
{ NULL, NULL }
};
#define MAXLINE 1000
#define COMMENT_CHAR '#'
#define streq(a, b) (strcmp(a, b) == 0)
#define new_array(T, n) ((T *)alloc(n, sizeof (T)))
#define new_struct(T) new_array(T, 1)
#define new_string(n) new_array(char, n)
static char line[MAXLINE]; /* current input line */
static int linenum = 0; /* configuration file line number */
static int confentries = 0; /* number of confinfo entries */
extern int dopt; /* debug option */
/*
* Process the configuration file. (See comments above.)
*
* Returns NULL if conffile is NULL.
* Returns vdconf if all is well.
* If there's a fatal error, this routine prints an error message and exits.
*/
struct vdconf *
getconfinfo(conffile)
char *conffile; /* pointer to configuration file name */
{
FILE *fp;
if (conffile == NULL)
return (NULL); /* no configuration file */
if ((fp = fopen(conffile, "r")) == NULL)
error("can't open configuration file");
while (!feof(fp))
parse_line(fp);
return (gen_vdconf(confinfo)); /* generate vdconf table */
}
static void
parse_line(fp)
FILE *fp;
{
struct confdispatch *cdp;
static struct confinfo *cip;
char *word;
int linelen;
linenum++;
if (fgets(line, MAXLINE, fp) == NULL)
return;
linelen = strlen(line);
if (linelen == MAXLINE - 1 && line[linelen] != '\n')
fatal("config file line %d too long\n", linenum);
word = getword(fp); /* get first word of next line */
if (word == NULL) /* empty line */
return;
/*
* dispatch to the appropriate configuration routine
*/
for (cdp = confdispatch; cdp->conftype != NULL; cdp++)
if (streq(cdp->conftype, word))
break;
if (cdp->conftype != NULL) {
if (confinfo == NULL)
confinfo = cip = getcip();
else {
cip->next = getcip();
cip = cip->next;
}
cip->linenum = linenum;
(*cdp->conffcn)(fp, cip);
confentries++;
} else
fatal("Unrecognized config type '%s' on line %d\n",
word, linenum);
}
/*
* Loop through all of the confinfo entries and produce the vdconf array.
*/
static struct vdconf *
gen_vdconf(confinfo)
struct confinfo *confinfo;
{
struct confinfo *cip;
struct vdconf *vdconf, *vdc;
vdconf = new_array(struct vdconf, confentries + 1);
vdconf[confentries].vdc_type = VDCEND; /* terminate table */
for (cip = confinfo, vdc = vdconf; cip; cip = cip->next, vdc++) {
vdc->vdc_type = cip->type;
switch (cip->type) {
#ifndef sun4c
case VDCCONTROLLER:
vdc->vdc_data = (long)gen_ctlr(cip);
break;
case VDCDEVICE:
vdc->vdc_data = (long)gen_device(cip);
break;
#endif /* sun4c */
case VDCBLOCKMAJOR:
vdc->vdc_data = (long)cip->majornum;
break;
case VDCCHARMAJOR:
vdc->vdc_data = (long)cip->majornum;
break;
case VDCSYSCALLNUM:
vdc->vdc_data = (long)cip->syscallnum;
break;
default:
fatal("Internal error: unknown type %x (line %d)\n",
cip->type, cip->linenum);
}
}
return (vdconf);
}
#ifndef sun4c
/*
* Generate an mb_ctlr entry for this controller.
*/
static struct mb_ctlr *
gen_ctlr(cip)
struct confinfo *cip;
{
struct mb_ctlr *mc;
struct confinfo *c;
if (dopt)
printf("gen_ctlr\n");
/*
* Check for duplicate controller names
*/
for (c = confinfo; c != NULL; c = c->next) {
if (c == cip || c->type != VDCCONTROLLER)
continue;
if (streq(c->name, cip->name))
fatal(
"Duplicate controller name '%s' (lines %d and %d)\n",
c->name, c->linenum, cip->linenum);
}
mc = new_struct(struct mb_ctlr);
mc->mc_ctlr = getnum(cip);
mc->mc_addr = (caddr_t)cip->csr;
mc->mc_intpri = cip->priority;
mc->mc_space = cip->space;
#ifdef sun386
mc->mc_intr = cip->irq;
mc->mc_dmachan = cip->dmachan;
#else
mc->mc_intr = gen_vector(cip->vectors);
#endif
return (mc);
}
/*
* Generate an mb_device entry for this device.
*/
static struct mb_device *
gen_device(cip)
struct confinfo *cip;
{
struct mb_device *md;
struct confinfo *c;
if (dopt)
printf("gen_device\n");
/*
* Check for duplicate devices on the same controller
*/
for (c = confinfo; c; c = c->next) {
if (c == cip || c->type != VDCDEVICE ||
c->drive != cip->drive ||
getctlrnum(c) != getctlrnum(cip))
continue;
if (streq(c->name, cip->name))
fatal(
"Duplicate device name '%s' (lines %d and %d)\n",
c->name, c->linenum, cip->linenum);
}
md = new_struct(struct mb_device);
md->md_unit = getnum(cip);
md->md_ctlr = getctlrnum(cip);
md->md_slave = cip->drive;
md->md_addr = (caddr_t)cip->csr;
md->md_intpri = cip->priority;
md->md_space = cip->space;
#ifdef sun386
md->md_intr = cip->irq;
md->md_dmachan = cip->dmachan;
#else
md->md_intr = gen_vector(cip->vectors);
#endif
return (md);
}
#ifndef sun386
static struct vec *
gen_vector(veclist)
struct veclist *veclist;
{
int nvec;
struct veclist *vp, *nvp;
struct vec *v, *v0;
int *ip;
nvec = 0;
for (vp = veclist; vp != NULL; vp = vp->next)
nvec++;
if (nvec == 0)
return (NULL);
v0 = v = new_array(struct vec, nvec + 1);
ip = new_array(int, nvec + 1);
for (vp = veclist; vp != NULL; vp = nvp) {
*v = vp->vec;
v->v_vptr = ip;
*ip++ = v->v_vec;
v++;
nvp = vp->next;
free((char *)vp);
}
v = NULL;
return ((struct vec *)v0);
}
#endif
/*
* Get the number from the controller or device name.
* For example, if the name is fdc2 then this routine will return 2.
*/
static int
getnum(cip)
struct confinfo *cip;
{
char *np = cip->name, *p;
long num;
while (*np != '\0' && !isdigit(*np))
np++;
if (*np == '\0')
fatal("missing unit number in '%s' on line %d\n",
cip->name, cip->linenum);
num = strtol(np, &p, 10);
if (*p != '\0')
fatal("Invalid device or controller name '%s' (line %d)\n",
cip->name, cip->linenum);
return (num);
}
/*
* Get the controller number for this device.
* returns -1 if there is no controller.
*/
static int
getctlrnum(cip)
struct confinfo *cip;
{
struct confinfo *c;
char *name, *cname;
int namelen;
if (dopt)
printf("getctlrnum\n");
if (cip->space != 0)
return (-1); /* no controller */
for (c = confinfo; c; c = c->next) {
if (c->type != VDCCONTROLLER)
continue;
if (streq(cip->conname, c->name)) {
/*
* We've found the controller that we're connected to.
* Now let's make sure that our name makes sense.
* For example, controller "fdc0" is supposed to have
* devices with names such as "fd0", "fd1", etc.
*/
name = getname(cip); /* get dev name without num */
cname = getname(c); /* get ctrl name without num */
namelen = strlen(name);
if (strncmp(name, cname, namelen) != 0 ||
cname[namelen] != 'c')
fatal(
"invalid device name '%s' for controller %s (lines %d and %d)\n",
cip->name, c->name, cip->linenum, c->linenum);
return (getnum(c));
}
}
fatal(
"Device '%s' is connected to an unknown controller '%s' (line %d)\n",
cip->name, cip->conname, cip->linenum);
/*NOTREACHED*/
}
/*
* Get the name (minus the number) of a controller or device.
*/
static char *
getname(cip)
struct confinfo *cip;
{
int len;
char *p;
if (dopt)
printf("getname\n");
p = cip->name;
while (*p != '\0' && !isdigit(*p))
p++;
len = p - cip->name;
p = new_string(len);
strncpy(p, cip->name, len);
p[len] = '\0';
return (p);
}
/*
* Generate a confinfo entry for a controller.
*/
static void
conf_controller(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("conf_controller\n");
cip->type = VDCCONTROLLER;
cip->name = getstring(fp);
read_literal(fp, "at");
getbusconn(fp, cip, VDCCONTROLLER); /* get bus that controller is on */
read_literal(fp, "?");
getoptions(fp, cip);
}
/*
* Generate a confinfo entry for a disk.
*/
static void
conf_disk(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("conf_disk\n");
conf_device(fp, cip);
cip->disk = 1;
}
/*
* Generate a confinfo entry for a device.
*/
static void
conf_device(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("conf_device\n");
cip->type = VDCDEVICE;
cip->name = getstring(fp); /* get device name and number */
read_literal(fp, "at");
getbusconn(fp, cip, VDCDEVICE); /* get bus connection */
if (cip->space != 0)
read_literal(fp, "?");
getoptions(fp, cip); /* get the rest of the options */
}
#endif /* sun4c */
/*
* Generate a confinfo entry for a blockmajor configuration line.
*/
static void
conf_blockmajor(fp, cip)
FILE *fp;
struct confinfo *cip;
{
cip->type = VDCBLOCKMAJOR;
cip->majornum = getint(fp, 0, 255);
}
/*
* Generate a confinfo entry for a charmajor configuration line.
*/
static void
conf_charmajor(fp, cip)
FILE *fp;
struct confinfo *cip;
{
cip->type = VDCCHARMAJOR;
cip->majornum = getint(fp, 0, 255);
}
/*
* Generate a confinfo entry for a system call number configuration line.
*/
static void
conf_syscallnum(fp, cip)
FILE *fp;
struct confinfo *cip;
{
cip->type = VDCSYSCALLNUM;
cip->syscallnum = getint(fp, 0, 255);
}
/*
* Allocate a confinfo entry for this line in the configuration file.
*/
static struct confinfo *
getcip()
{
struct confinfo *cip;
cip = new_struct(struct confinfo);
bzero((char *)cip, sizeof (struct confinfo));
return (cip);
}
#ifndef sun4c
/*
* Get a bus connection. For a controller it must be on the names
* in the table "bustype". For a device it can either be one of
* names in the table "bustype" or the name of a controller.
*/
static void
getbusconn(fp, cip, type)
FILE *fp;
struct confinfo *cip;
int type;
{
register struct bustype *btp;
cip->conname = getstring(fp); /* get bus connection */
for (btp = bustype; btp->name != NULL; btp++) {
if (streq(btp->name, cip->conname))
break;
}
if (btp->name != NULL)
cip->space = btp->type;
else if (type == VDCCONTROLLER) {
fatal("Unrecognized bus type '%s' on line %d\n",
cip->conname, cip->linenum);
} else
cip->space = 0;
}
#endif /* sun4c */
/*
* Copy a string from the configuration file.
*/
static char *
getstring(fp)
FILE *fp;
{
char *word;
word = getword(fp);
if (word == NULL)
fatal("missing string at line %d\n", linenum);
return (strdup(word));
}
/*
* Get an integer from the next word in the configuration file.
*/
static int
getint(fp, min, max)
FILE *fp;
int min, max;
{
char *word, *p;
long n;
if (dopt)
printf("getint\n");
word = getword(fp);
if (word == NULL)
fatal("missing integer at line %d\n", linenum);
n = strtol(word, &p, 0);
if (*p != '\0' || (min != -1 && n < min) || (max != -1 && n > max))
fatal("Invalid number '%s' on line %d\n", word, linenum);
if (dopt)
printf("word: %s, value %x\n", word, n);
return (n);
}
#ifndef sun4c
/*
* Get device and controller options.
*/
static int
getoptions(fp, cip)
FILE *fp;
register struct confinfo *cip;
{
register struct options *opt;
char *word;
if (dopt)
printf("getoptions\n");
while ((word = getword(fp)) != NULL) {
if (dopt)
printf("option: %s\n", word);
for (opt = options; opt->name != NULL; opt++) {
if (streq(opt->name, word))
break;
}
if (opt->name != NULL)
(*opt->optfcn)(fp, cip);
else
fatal("Unrecognized keyword '%s' on line %d\n",
word, cip->linenum);
}
}
/*
* Get the drive number.
*/
static void
getdrive(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getdrive\n");
cip->drive = getint(fp, 0, 255);
}
/*
* Get the csr.
*/
static void
getcsr(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getcsr\n");
cip->csr = getint(fp, -1, -1);
}
/*
* Get the interrupt request number.
*/
static void
getirq(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getirq\n");
cip->irq = getint(fp, 0, 15);
}
/*
* Get VME interrupt vector
*/
static void
getvector(fp, cip)
FILE *fp;
struct confinfo *cip;
{
struct veclist *v;
char *handler_name;
struct nlist n[2];
int vecno;
if (dopt)
printf("getvector\n");
v = new_struct(struct veclist);
handler_name = getstring(fp);
vecno = getint(fp, 0x40, 0xff);
bzero((char *)n, sizeof (n));
n[0].n_name = new_string(strlen(handler_name) + 2);
n[0].n_name[0] = '_';
strcpy(&n[0].n_name[1], handler_name);
if (nlist(outputfile, n) == -1)
fatal("can't read %s symbol table\n", outputfile);
if ((n[0].n_type & N_TYPE) != N_TEXT)
fatal("lookup of %s failed\n", handler_name);
v->vec.v_func = (int (*)()) n[0].n_value;
v->vec.v_vec = vecno;
v->vec.v_vptr = NULL;
v->next = cip->vectors;
cip->vectors = v;
}
/*
* Get the interrupt priority.
*/
static void
getpriority(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getpriority\n");
cip->priority = getint(fp, 0, 7);
}
/*
* Get the dma channel.
*/
static void
getdmachan(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getdmachan\n");
cip->dmachan = getint(fp, -1, -1);
}
/*
* Get the flags.
*/
static void
getflags(fp, cip)
FILE *fp;
struct confinfo *cip;
{
if (dopt)
printf("getflags\n");
cip->flags = getint(fp, -1, -1);
}
#endif /* sun4c */
/*
* Allocate a block of memory.
*/
static char *
alloc(nelem, elsize)
u_int nelem, elsize;
{
register char *addr = calloc(nelem, elsize);
if (addr == NULL)
fatal("out of memory (size = %x)\n", nelem * elsize);
return (addr);
}
/*
* getword
* return next word of current input line; returns NULL on EOF,
* end of line, or comment.
*/
/*ARGSUSED*/
static char *
getword(fp)
FILE *fp;
{
static char *word = NULL;
word = strtok((word == NULL) ? line : NULL, " \t\n");
if (word != NULL && *word == COMMENT_CHAR)
word = NULL; /* skip comments */
return (word);
}
static void
read_literal(fp, literal)
FILE *fp;
char *literal;
{
char *s = getword(fp);
if (s == NULL || !streq(s, literal))
fatal("missing keyword '%s' at line %d\n", literal, linenum);
}