Index: mkheaders.c
===================================================================
--- mkheaders.c (.../vendor/netbsd/current-20080118)    (revision 51)
+++ mkheaders.c (.../tags/patch-1-rc1)  (revision 51)
@@ -162,6 +162,7 @@
       struct nvlist *nv;
       FILE *fp;

+
       (void)snprintf(nfname, sizeof(nfname), "%s.h", head->nv_name);
       (void)snprintf(tfname, sizeof(tfname), "tmp_%s", nfname);

@@ -376,17 +377,31 @@
       const char *tfname;
       FILE *tfp;
       struct devbase *d;
+       struct nvlist *nv;
+       struct nvlist *lkmlist = NULL;  /* to remember LKM devices */

       tfname = "tmp_ioconf.h";
       if ((tfp = fopen(tfname, "w")) == NULL)
               return (herr("open", tfname, NULL));

       TAILQ_FOREACH(d, &allbases, d_next) {
-               if (!devbase_has_instances(d, WILD))
-                       continue;
-               fprintf(tfp, "extern struct cfdriver %s_cd;\n", d->d_name);
+               if (devbase_has_instances(d, WILD))
+                       fprintf(tfp, "extern struct cfdriver %s_cd;\n", d->d_name);
+               else if (devbase_has_any_instance(d, WILD, DEVI_MODULE, 0))
+                       lkmlist = newnv(d->d_name, NULL, d, 0, lkmlist);
       }

+       if (lkmlist) {
+               fprintf(tfp, "\n/* for LKMs */\n");
+               for (nv = lkmlist; nv; nv = nv->nv_next) {
+                       d = nv->nv_ptr;
+                       fprintf(tfp, "extern struct cfdriver %s_cd;\n",
+                               d->d_name);
+               }
+               nvfreel(lkmlist);
+               lkmlist = NULL;
+       }
+
       fflush(tfp);
       if (ferror(tfp))
               return herr("writ", tfname, tfp);
Index: mkmakefile.c
===================================================================
--- mkmakefile.c        (.../vendor/netbsd/current-20080118)    (revision 51)
+++ mkmakefile.c        (.../tags/patch-1-rc1)  (revision 51)
@@ -64,52 +64,103 @@
static const char *filetype_prologue(struct filetype *);


-static void emitdefs(FILE *);
+static void emitdefs(FILE *, const char *);
static void emitfiles(FILE *, int, int);

-static void emitobjs(FILE *);
-static void emitcfiles(FILE *);
-static void emitsfiles(FILE *);
-static void emitrules(FILE *);
-static void emitload(FILE *);
-static void emitincludes(FILE *);
-static void emitappmkoptions(FILE *);
+static void emitobjs(FILE *, void *);
+static void emitcfiles(FILE *, void *);
+static void emitsfiles(FILE *, void *);
+static void emitrules(FILE *, void *);
+static void emitload(FILE *, void *);
+static void emitincludes(FILE *, void *);
+static void emitappmkoptions(FILE *, void *);

int
mkmakefile(void)
{
+       static const struct makefile_constructs c[] = {
+               { "OBJS", emitobjs },
+               { "CFILES", emitcfiles },
+               { "SFILES", emitsfiles },
+               { "RULES", emitrules },
+               { "LOAD", emitload },
+               { "INCLUDES", emitincludes },
+               { "MAKEOPTIONSAPPEND", emitappmkoptions },
+               { "KMOD", emitkmod },
+               { NULL, NULL }
+       };
+
+       return mkmakefile_common(c, "", ".", NULL);
+}
+
+int
+mkmakefile_common(const struct makefile_constructs *cset,
+                 const char *template_suffix,
+                 const char *output_dir,
+                 void *aux)
+{
       FILE *ifp, *ofp;
       int lineno;
-       void (*fn)(FILE *);
       char *ifname;
-       char line[BUFSIZ], buf[200];
+       char line[BUFSIZ];
+       char buf[MAXPATHLEN];
+       char makefilename[MAXPATHLEN], tempfilename[MAXPATHLEN];
+       const char *srcdirprepend = "";
+       int i;
+       int lkm = aux != NULL;

       /* Try a makefile for the port first.
        */
-       (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile.%s",
-           machine, machine);
+       (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile%s.%s",
+           machine, template_suffix, machine);
       ifname = sourcepath(buf);
       if ((ifp = fopen(ifname, "r")) == NULL) {
               /* Try a makefile for the architecture second.
                */
-               (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile.%s",
-                   machinearch, machinearch);
+               (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile%s.%s",
+                   machinearch, template_suffix, machinearch);
               free(ifname);
               ifname = sourcepath(buf);
               ifp = fopen(ifname, "r");
       }
+       if (ifp == NULL && lkm) {
+               /* for LKM makefile, try MI template */
+               (void)snprintf(buf, sizeof buf, "conf/Makefile%s",
+                              template_suffix);
+               ifname = sourcepath(buf);
+               ifp = fopen(ifname, "r");
+       }
       if (ifp == NULL) {
               warn("cannot read %s", ifname);
               goto bad2;
       }

-       if ((ofp = fopen("Makefile.tmp", "w")) == NULL) {
-               warn("cannot write Makefile");
+
+       snprintf(makefilename, sizeof buf, "%s/Makefile", output_dir);
+       snprintf(tempfilename, sizeof tempfilename, "%s.tmp", makefilename);
+
+       if ((ofp = fopen(tempfilename, "w")) == NULL) {
+               warn("cannot write %s", tempfilename);
               goto bad1;
       }

-       emitdefs(ofp);
+       if (*srcdir == '/')
+               srcdirprepend = "";
+       else if (lkm)
+               srcdirprepend = "../../";
+       else {
+               /*
+                * libkern and libcompat "Makefile.inc"s want relative S
+                * specification to begin with '.'.
+                */
+               if (*srcdir == '.')
+                       srcdirprepend = "";
+               else
+                       srcdirprepend = "./";
+       }

+       emitdefs(ofp, srcdirprepend);
+
       lineno = 0;
       while (fgets(line, sizeof(line), ifp) != NULL) {
               lineno++;
@@ -117,26 +168,21 @@
                       fputs(line, ofp);
                       continue;
               }
-               if (strcmp(line, "%OBJS\n") == 0)
-                       fn = emitobjs;
-               else if (strcmp(line, "%CFILES\n") == 0)
-                       fn = emitcfiles;
-               else if (strcmp(line, "%SFILES\n") == 0)
-                       fn = emitsfiles;
-               else if (strcmp(line, "%RULES\n") == 0)
-                       fn = emitrules;
-               else if (strcmp(line, "%LOAD\n") == 0)
-                       fn = emitload;
-               else if (strcmp(line, "%INCLUDES\n") == 0)
-                       fn = emitincludes;
-               else if (strcmp(line, "%MAKEOPTIONSAPPEND\n") == 0)
-                       fn = emitappmkoptions;
-               else {
+               for (i=0; cset[i].str != NULL; ++i) {
+                       /* chomp line */
+                       char *p = strchr(line+1, '\n');
+                       if (p)
+                               *p = '\0';
+
+                       if (strcmp(line+1, cset[i].str) == 0) {
+                               cset[i].func(ofp, aux);
+                               break;
+                       }
+               }
+               if (cset[i].str == NULL) {
                       cfgxerror(ifname, lineno,
                           "unknown %% construct ignored: %s", line);
-                       continue;
               }
-               (*fn)(ofp);
       }

       fflush(ofp);
@@ -154,21 +200,21 @@
       }
       (void)fclose(ifp);

-       if (moveifchanged("Makefile.tmp", "Makefile") != 0) {
-               warn("error renaming Makefile");
+       if (moveifchanged(tempfilename, makefilename) != 0) {
+               warn("error renaming %s", makefilename);
               goto bad2;
       }
       free(ifname);
       return (0);

 wrerror:
-       warn("error writing Makefile");
+       warn("error writing %s", makefilename);
 bad:
       if (ofp != NULL)
               (void)fclose(ofp);
 bad1:
       (void)fclose(ifp);
-       /* (void)unlink("Makefile.tmp"); */
+       /* (void)unlink(tempfilename); */
 bad2:
       free(ifname);
       return (1);
@@ -219,7 +265,7 @@
}

static void
-emitdefs(FILE *fp)
+emitdefs(FILE *fp, const char *srcdirprepend)
{
       struct nvlist *nv;
       char *sp;
@@ -240,21 +286,14 @@
       putc('\n', fp);
       fprintf(fp, "PARAM=-DMAXUSERS=%d\n", maxusers);
       fprintf(fp, "MACHINE=%s\n", machine);
-       if (*srcdir == '/' || *srcdir == '.') {
-               fprintf(fp, "S=\t%s\n", srcdir);
-       } else {
-               /*
-                * libkern and libcompat "Makefile.inc"s want relative S
-                * specification to begin with '.'.
-                */
-               fprintf(fp, "S=\t./%s\n", srcdir);
-       }
+       fprintf(fp, "S=\t%s%s\n", srcdirprepend, srcdir);
       for (nv = mkoptions; nv != NULL; nv = nv->nv_next)
               fprintf(fp, "%s=%s\n", nv->nv_name, nv->nv_str);
}

static void
-emitobjs(FILE *fp)
+/*ARGSUSED*/
+emitobjs(FILE *fp, void *aux)
{
       struct files *fi;
       struct objects *oi;
@@ -314,14 +353,16 @@
}

static void
-emitcfiles(FILE *fp)
+/*ARGSUSED*/
+emitcfiles(FILE *fp, void *aux)
{

       emitfiles(fp, 'c', 0);
}

static void
-emitsfiles(FILE *fp)
+/*ARGSUSED*/
+emitsfiles(FILE *fp, void *aux)
{

       emitfiles(fp, 's', 'S');
@@ -402,7 +443,8 @@
 * Emit the make-rules.
 */
static void
-emitrules(FILE *fp)
+/*ARGSUSED*/
+emitrules(FILE *fp, void *aux)
{
       struct files *fi;
       const char *cp, *fpath;
@@ -445,7 +487,8 @@
 * This function is not to be called `spurt'.
 */
static void
-emitload(FILE *fp)
+/*ARGSUSED*/
+emitload(FILE *fp, void *aux)
{
       struct config *cf;
       const char *nm, *swname;
@@ -475,7 +518,8 @@
 * Emit include headers (for any prefixes encountered)
 */
static void
-emitincludes(FILE *fp)
+/*ARGSUSED*/
+emitincludes(FILE *fp, void *aux)
{
       struct prefix *pf;

@@ -504,7 +548,8 @@
 * Emit appending makeoptions.
 */
static void
-emitappmkoptions(FILE *fp)
+/*ARGSUSED*/
+emitappmkoptions(FILE *fp, void *aux)
{
       struct nvlist *nv;

Index: mkioconf.c
===================================================================
--- mkioconf.c  (.../vendor/netbsd/current-20080118)    (revision 51)
+++ mkioconf.c  (.../tags/patch-1-rc1)  (revision 51)
@@ -61,7 +61,7 @@
static void emitcfdrivers(FILE *);
static void emitexterns(FILE *);
static void emitcfattachinit(FILE *);
-static void emithdr(FILE *);
+//static void emithdr(FILE *);
static void emitloc(FILE *);
static void emitpseudo(FILE *);
static void emitparents(FILE *);
@@ -89,7 +89,7 @@
               return (1);
       }

-       emithdr(fp);
+       ioconf_emithdr(fp, "ioconf.c");
       emitcfdrivers(fp);
       emitexterns(fp);
       emitcfattachinit(fp);
@@ -129,15 +129,15 @@
       return (n1 - n2);
}

-static void
-emithdr(FILE *ofp)
+void
+ioconf_emithdr(FILE *ofp, const char *filename)
{
       FILE *ifp;
       int n;
       char ifnbuf[200], buf[BUFSIZ];
       char *ifn;

-       autogen_comment(ofp, "ioconf.c");
+       autogen_comment(ofp, filename);

       (void)snprintf(ifnbuf, sizeof(ifnbuf), "arch/%s/conf/ioconf.incl.%s",
           machine, machine);
@@ -174,7 +174,7 @@

       if (a->a_locs) {
               fprintf(fp,
-                   "static const struct cfiattrdata %scf_iattrdata = {\n",
+                   "const struct cfiattrdata %scf_iattrdata = {\n",
                           name);
               fprintf(fp, "\t\"%s\", %d,\n\t{\n", name, a->a_loclen);
               for (nv = a->a_locs; nv; nv = nv->nv_next)
@@ -185,7 +185,7 @@
               fprintf(fp, "\t}\n};\n");
       } else {
               fprintf(fp,
-                   "static const struct cfiattrdata %scf_iattrdata = {\n"
+                   "const struct cfiattrdata %scf_iattrdata = {\n"
                   "\t\"%s\", 0, {\n\t\t{ NULL, NULL, 0 },\n\t}\n};\n",
                   name, name);
       }
@@ -197,9 +197,6 @@
emitcfdrivers(FILE *fp)
{
       struct devbase *d;
-       struct nvlist *nv;
-       struct attr *a;
-       int has_iattrs;

       NEWLINE;
       ht_enumerate(attrtab, cf_locators_print, fp);
@@ -208,28 +205,7 @@
       TAILQ_FOREACH(d, &allbases, d_next) {
               if (!devbase_has_instances(d, WILD))
                       continue;
-               has_iattrs = 0;
-               for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
-                       a = nv->nv_ptr;
-                       if (a->a_iattr == 0)
-                               continue;
-                       if (has_iattrs == 0)
-                               fprintf(fp,
-                                   "static const struct cfiattrdata * const %s_attrs[] = { ",
-                                   d->d_name);
-                       has_iattrs = 1;
-                       fprintf(fp, "&%scf_iattrdata, ", a->a_name);
-               }
-               if (has_iattrs)
-                       fprintf(fp, "NULL };\n");
-               fprintf(fp, "CFDRIVER_DECL(%s, %s, ", d->d_name, /* ) */
-                   d->d_classattr != NULL ? d->d_classattr->a_devclass
-                                          : "DV_DULL");
-               if (has_iattrs)
-                       fprintf(fp, "%s_attrs", d->d_name);
-               else
-                       fprintf(fp, "NULL");
-               fprintf(fp, /* ( */ ");\n\n");
+               ioconf_emit_cfdriver(d, fp);
       }

       NEWLINE;
@@ -242,6 +218,63 @@
       fprintf(fp, "\tNULL\n};\n");
}

+
+/* This function is used for LKM ioconf */
+void
+ioconf_emit_ref_iattrs(struct devbase *d, FILE *fp)
+{
+       struct nvlist *nv;
+       struct attr *a;
+
+       for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
+               a = nv->nv_ptr;
+               if (a->a_iattr == 0)
+                       continue;
+
+               a = nv->nv_ptr;
+               fprintf(fp,
+                       "extern const struct cfiattrdata %scf_iattrdata;\n",
+                       a->a_name);
+       }
+}
+
+void
+ioconf_emit_cfdriver(struct devbase *d, FILE *fp)
+{
+       struct nvlist *nv;
+       struct attr *a;
+       int has_iattrs;
+
+       has_iattrs = 0;
+       for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
+               a = nv->nv_ptr;
+               if (a->a_iattr == 0)
+                       continue;
+               if (!has_iattrs) {
+                       fprintf(fp,
+                               "const struct cfiattrdata * const %s_attrs[] = { ",
+                               d->d_name);
+               }
+
+               a = nv->nv_ptr;
+               fprintf(fp, "&%scf_iattrdata, ", a->a_name);
+               has_iattrs = 1;
+       }
+
+       if (has_iattrs)
+               fprintf(fp, "NULL };\n");
+
+       fprintf(fp, "CFDRIVER_DECL(%s, %s, ", d->d_name, /* ) */
+               d->d_classattr != NULL ? d->d_classattr->a_devclass
+               : "DV_DULL");
+       if (has_iattrs)
+               fprintf(fp, "%s_attrs", d->d_name);
+       else
+               fprintf(fp, "NULL");
+       fprintf(fp, /* ( */ ");\n\n");
+}
+
+
static void
emitexterns(FILE *fp)
{
@@ -324,23 +357,32 @@

       NEWLINE;
       TAILQ_FOREACH(p, &allpspecs, p_list) {
-               if (p->p_devs == NULL || p->p_active != DEVI_ACTIVE)
-                       continue;
-               fprintf(fp,
-                   "static const struct cfparent pspec%d = {\n", p->p_inst);
-               fprintf(fp, "\t\"%s\", ", p->p_iattr->a_name);
-               if (p->p_atdev != NULL) {
-                       fprintf(fp, "\"%s\", ", p->p_atdev->d_name);
-                       if (p->p_atunit == WILD)
-                               fprintf(fp, "DVUNIT_ANY");
-                       else
-                               fprintf(fp, "%d", p->p_atunit);
-               } else
-                       fprintf(fp, "NULL, 0");
-               fprintf(fp, "\n};\n");
+               ioconf_emit_pspec(fp, p, 0);
       }
}

+void
+ioconf_emit_pspec(FILE *fp, struct pspec *p, int force)
+{
+       /* force==1 when creating _MODULE_ioconf.c */
+       if (!force &&
+           (p->p_devs == NULL || p->p_active != DEVI_ACTIVE))
+               return;
+
+       fprintf(fp,
+               "static const struct cfparent pspec%d = {\n", p->p_inst);
+       fprintf(fp, "\t\"%s\", ", p->p_iattr->a_name);
+       if (p->p_atdev != NULL) {
+               fprintf(fp, "\"%s\", ", p->p_atdev->d_name);
+               if (p->p_atunit == WILD)
+                       fprintf(fp, "DVUNIT_ANY");
+               else
+                       fprintf(fp, "%d", p->p_atunit);
+       } else
+               fprintf(fp, "NULL, 0");
+       fprintf(fp, "\n};\n");
+}
+
/*
 * Emit the cfdata array.
 */
@@ -348,6 +390,32 @@
emitcfdata(FILE *fp)
{
       struct devi **p, *i;
+       const char *dataname = "cfdata";
+
+       ioconf_cfdata_start(fp, dataname);
+       for (p = packed; (i = *p) != NULL; p++) {
+               /* the description */
+               ioconf_cfdata_entry(fp, i);
+       }
+
+       ioconf_cfdata_end(fp, dataname);
+}
+
+void
+ioconf_cfdata_start(FILE *fp, const char *array_name)
+{
+       fprintf(fp, "\n"
+               "#define NORM FSTATE_NOTFOUND\n"
+               "#define STAR FSTATE_STAR\n"
+               "\n"
+               "struct cfdata %s[] = {\n"
+               "    /* driver           attachment    unit state "
+               "loc   flags pspec */\n", array_name);
+}
+
+void
+ioconf_cfdata_entry(FILE *fp, struct devi *i)
+{
       struct pspec *ps;
       int unit, v;
       const char *state, *basename, *attachment;
@@ -357,78 +425,75 @@
       char locbuf[20];
       const char *lastname = "";

-       fprintf(fp, "\n"
-               "#define NORM FSTATE_NOTFOUND\n"
-               "#define STAR FSTATE_STAR\n"
-               "\n"
-               "struct cfdata cfdata[] = {\n"
-               "    /* driver           attachment    unit state "
-               "loc   flags pspec */\n");
-       for (p = packed; (i = *p) != NULL; p++) {
-               /* the description */
-               fprintf(fp, "/*%3d: %s at ", i->i_cfindex, i->i_name);
-               if ((ps = i->i_pspec) != NULL) {
-                       if (ps->p_atdev != NULL &&
-                           ps->p_atunit != WILD) {
-                               fprintf(fp, "%s%d", ps->p_atdev->d_name,
-                                           ps->p_atunit);
-                       } else if (ps->p_atdev != NULL) {
-                               fprintf(fp, "%s?", ps->p_atdev->d_name);
+       fprintf(fp, "/*%3d: %s at ", i->i_cfindex, i->i_name);
+       if ((ps = i->i_pspec) != NULL) {
+               if (ps->p_atdev != NULL &&
+                   ps->p_atunit != WILD) {
+                       fprintf(fp, "%s%d", ps->p_atdev->d_name,
+                               ps->p_atunit);
+               } else if (ps->p_atdev != NULL) {
+                       fprintf(fp, "%s?", ps->p_atdev->d_name);
+               } else {
+                       fprintf(fp, "%s?", ps->p_iattr->a_name);
+               }
+
+               a = ps->p_iattr;
+               for (nv = a->a_locs, v = 0; nv != NULL;
+                    nv = nv->nv_next, v++) {
+                       if (ARRNAME(nv->nv_name, lastname)) {
+                               fprintf(fp, " %s %s",
+                                       nv->nv_name, i->i_locs[v]);
                       } else {
-                               fprintf(fp, "%s?", ps->p_iattr->a_name);
+                               fprintf(fp, " %s %s",
+                                       nv->nv_name,
+                                       i->i_locs[v]);
+                               lastname = nv->nv_name;
                       }
-
-                       a = ps->p_iattr;
-                       for (nv = a->a_locs, v = 0; nv != NULL;
-                            nv = nv->nv_next, v++) {
-                               if (ARRNAME(nv->nv_name, lastname)) {
-                                       fprintf(fp, " %s %s",
-                                           nv->nv_name, i->i_locs[v]);
-                               } else {
-                                       fprintf(fp, " %s %s",
-                                                   nv->nv_name,
-                                                   i->i_locs[v]);
-                                       lastname = nv->nv_name;
-                               }
-                       }
-               } else {
-                       a = NULL;
-                       fputs("root", fp);
               }
+       } else {
+               a = NULL;
+               fputs("root", fp);
+       }

-               fputs(" */\n", fp);
+       fputs(" */\n", fp);

-               /* then the actual defining line */
-               basename = i->i_base->d_name;
-               attachment = i->i_atdeva->d_name;
-               if (i->i_unit == STAR) {
-                       unit = i->i_base->d_umax;
-                       state = "STAR";
-               } else {
-                       unit = i->i_unit;
-                       state = "NORM";
-               }
-               if (i->i_locoff >= 0) {
-                       (void)snprintf(locbuf, sizeof(locbuf), "loc+%3d",
-                           i->i_locoff);
-                       loc = locbuf;
-               } else
-                       loc = "loc";
-               fprintf(fp, "    {\"%s\",%s\"%s\",%s%2d, %s, %7s, %#6x, ",
-                           basename, strlen(basename) < 8 ? "\t\t"
-                                                          : "\t",
-                           attachment, strlen(attachment) < 5 ? "\t\t"
-                                                              : "\t",
-                           unit, state, loc, i->i_cfflags);
-               if (ps != NULL)
-                       fprintf(fp, "&pspec%d},\n", ps->p_inst);
-               else
-                       fputs("NULL},\n", fp);
+       /* then the actual defining line */
+       basename = i->i_base->d_name;
+       attachment = i->i_atdeva->d_name;
+       if (i->i_unit == STAR) {
+               unit = i->i_base->d_umax;
+               state = "STAR";
+       } else {
+               unit = i->i_unit;
+               state = "NORM";
       }
+       if (i->i_locoff >= 0) {
+               (void)snprintf(locbuf, sizeof(locbuf), "loc+%3d",
+                              i->i_locoff);
+               loc = locbuf;
+       } else
+               loc = "loc";
+       fprintf(fp, "    {\"%s\",%s\"%s\",%s%2d, %s, %7s, %#6x, ",
+               basename, strlen(basename) < 8 ? "\t\t"
+               : "\t",
+               attachment, strlen(attachment) < 5 ? "\t\t"
+               : "\t",
+               unit, state, loc, i->i_cfflags);
+       if (ps != NULL)
+               fprintf(fp, "&pspec%d},\n", ps->p_inst);
+       else
+               fputs("NULL},\n", fp);
+}
+
+void
+/*ARGSUSED*/
+ioconf_cfdata_end(FILE *fp, const char *array_name)
+{
       fprintf(fp, "    {%s,%s%s,%s%2d, %s, %7s, %#6x, %s}\n};\n",
           "NULL", "\t\t", "NULL", "\t\t", 0, "0", "NULL", 0, "NULL");
}

+
/*
 * Emit the table of potential roots.
 */
@@ -461,11 +526,15 @@

       fputs("\n/* pseudo-devices */\n", fp);
       TAILQ_FOREACH(i, &allpseudo, i_next) {
+               if (i->i_module)
+                       continue;
               fprintf(fp, "void %sattach(int);\n",
                   i->i_base->d_name);
       }
       fputs("\nstruct pdevinit pdevinit[] = {\n", fp);
       TAILQ_FOREACH(i, &allpseudo, i_next) {
+               if (i->i_module)
+                       continue;
               d = i->i_base;
               fprintf(fp, "\t{ %sattach, %d },\n",
                   d->d_name, d->d_umax);
Index: gram.y
===================================================================
--- gram.y      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ gram.y      (.../tags/patch-1-rc1)  (revision 51)
@@ -108,7 +108,7 @@
%token ENDFILE
%token XFILE FILE_SYSTEM FLAGS
%token IDENT
-%token XMACHINE MAJOR MAKEOPTIONS MAXUSERS MAXPARTITIONS MINOR
+%token XMACHINE MAJOR MAKEOPTIONS MAXUSERS MAXPARTITIONS MINOR MODULE
%token NEEDS_COUNT NEEDS_FLAG NO
%token XOBJECT OBSOLETE ON OPTIONS
%token PACKAGE PLUSEQ PREFIX PSEUDO_DEVICE
@@ -453,7 +453,8 @@

config_spec:
       one_def |
-       NO FILE_SYSTEM no_fs_list |
+       NO opt_module FILE_SYSTEM no_fs_list |
+       MODULE FILE_SYSTEM mod_fs_list |
       FILE_SYSTEM fs_list |
       NO MAKEOPTIONS no_mkopt_list |
       MAKEOPTIONS mkopt_list |
@@ -464,22 +465,34 @@
       CONFIG conf root_spec sysparam_list
                                       { addconf(&conf); } |
       NO CONFIG WORD                  { delconf($3); } |
-       NO PSEUDO_DEVICE WORD           { delpseudo($3); } |
-       PSEUDO_DEVICE WORD npseudo      { addpseudo($2, $3); } |
-       NO device_instance AT attachment
-                                       { deldevi($2, $4); } |
+       NO opt_module PSEUDO_DEVICE WORD        { delpseudo($4); } |
+       PSEUDO_DEVICE WORD npseudo
+                                       { addpseudo($2, $3, 0); } |
+       MODULE PSEUDO_DEVICE WORD npseudo
+                                       { addpseudo($3, $4, 1); } |
+       NO opt_module device_instance AT attachment
+                                       { deldevi($3, $5); } |
       NO DEVICE AT attachment         { deldeva($4); } |
-       NO device_instance              { deldev($2); } |
+       NO opt_module device_instance   { deldev($3); } |
       device_instance AT attachment locators flags_opt
-                                       { adddev($1, $3, $4, $5); };
+                                       { adddev($1, $3, $4, $5, 0); } |
+       MODULE device_instance AT attachment locators flags_opt
+                                       { adddev($2, $4, $5, $6, 1); };

fs_list:
       fs_list ',' fsoption |
       fsoption;

fsoption:
-       WORD                            { addfsoption($1); };
+       WORD                            { addfsoption($1, 0); };

+mod_fs_list:
+       mod_fs_list ',' mod_fsoption |
+       mod_fsoption;
+
+mod_fsoption:
+       WORD                            { addfsoption($1, 1); };
+
no_fs_list:
       no_fs_list ',' no_fsoption |
       no_fsoption;
@@ -590,6 +603,9 @@
       FLAGS NUMBER                    { $$ = $2.val; } |
       /* empty */                     { $$ = 0; };

+opt_module:
+       MODULE | /* empty */;
+
%%

void
Index: util.c
===================================================================
--- util.c      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ util.c      (.../tags/patch-1-rc1)  (revision 51)
@@ -45,6 +45,7 @@
#endif

#include <sys/types.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
@@ -52,6 +53,7 @@
#include <stdarg.h>
#include <util.h>
#include <err.h>
+#include <errno.h>
#include "defs.h"

static void cfgvxerror(const char *, int, const char *, va_list)
@@ -295,3 +297,21 @@
           " */\n\n",
           targetfile, conffile);
}
+
+/*
+ * Create a directory if not exists.  Die when fails.
+ */
+void
+make_directory(const char *path)
+{
+       struct stat st;
+
+       if (stat(path, &st) == -1) {
+               if (errno != ENOENT)
+                       warn("can't find %s", path);
+               if (mkdir(path, 0777) == -1)
+                       err(EXIT_FAILURE, "cannot create %s", path);
+       } else if (!S_ISDIR(st.st_mode))
+               errx(EXIT_FAILURE, "%s is not a directory", path);
+}
+
Index: mkdevsw.c
===================================================================
--- mkdevsw.c   (.../vendor/netbsd/current-20080118)    (revision 51)
+++ mkdevsw.c   (.../tags/patch-1-rc1)  (revision 51)
@@ -109,7 +109,8 @@

       for (i = 0 ; i <= maxbdevm ; i++) {
               (void)snprintf(mstr, sizeof(mstr), "%d", i);
-               if ((dm = ht_lookup(bdevmtab, intern(mstr))) == NULL)
+               if ((dm = ht_lookup(bdevmtab, intern(mstr))) == NULL ||
+                   dm->dm_active != DEVM_INKERNEL)
                       continue;

               fprintf(fp, "extern const struct bdevsw %s_bdevsw;\n",
@@ -120,7 +121,8 @@

       for (i = 0 ; i <= maxbdevm ; i++) {
               (void)snprintf(mstr, sizeof(mstr), "%d", i);
-               if ((dm = ht_lookup(bdevmtab, intern(mstr))) == NULL) {
+               if ((dm = ht_lookup(bdevmtab, intern(mstr))) == NULL ||
+                   dm->dm_active != DEVM_INKERNEL) {
                       fprintf(fp, "\tNULL,\n");
               } else {
                       fprintf(fp, "\t&%s_bdevsw,\n", dm->dm_name);
@@ -136,7 +138,8 @@

       for (i = 0 ; i <= maxcdevm ; i++) {
               (void)snprintf(mstr, sizeof(mstr), "%d", i);
-               if ((dm = ht_lookup(cdevmtab, intern(mstr))) == NULL)
+               if ((dm = ht_lookup(cdevmtab, intern(mstr))) == NULL ||
+                   dm->dm_active != DEVM_INKERNEL)
                       continue;

               fprintf(fp, "extern const struct cdevsw %s_cdevsw;\n",
@@ -147,7 +150,8 @@

       for (i = 0 ; i <= maxcdevm ; i++) {
               (void)snprintf(mstr, sizeof(mstr), "%d", i);
-               if ((dm = ht_lookup(cdevmtab, intern(mstr))) == NULL) {
+               if ((dm = ht_lookup(cdevmtab, intern(mstr))) == NULL ||
+                   dm->dm_active != DEVM_INKERNEL) {
                       fprintf(fp, "\tNULL,\n");
               } else {
                       fprintf(fp, "\t&%s_cdevsw,\n", dm->dm_name);
Index: lkm.c
===================================================================
--- lkm.c       (.../vendor/netbsd/current-20080118)    (revision 0)
+++ lkm.c       (.../tags/patch-1-rc1)  (revision 51)
@@ -0,0 +1,1573 @@
+/*-
+ * Copyright (c) 2007, 2008 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Hiroyuki Bessho.
+ *
+ * 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. 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#ifndef MAKE_BOOTSTRAP
+#include <sys/cdefs.h>
+#define        COPYRIGHT(x)    __COPYRIGHT(x)
+#else
+#define        COPYRIGHT(x)    static const char copyright[] = x
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+#include <assert.h>
+
+#include "defs.h"
+#include "sem.h"               /* for expandattr */
+
+#define        KMODDIR "modules"
+#define        KMODWORK_SUFFIX ".work"
+
+static int fixlkm_devbase(struct devbase *);
+static int fixlkm_deva(struct deva *);
+static int fixlkm_devi(struct devi *);
+static struct module *newmodule(const char *, enum module_type);
+static struct module *getmodule(const char *, enum module_type);
+static int lkm_select_device(struct module *, void (*)(struct attr *, void *));
+static int lkm_select_cb(const char *, void *, void *);
+static void lkm_select_attr(struct attr *, void *);
+static void lkm_record_require(struct attr *, void *);
+//static int for_all_module_files(struct module *, int (* )(struct files *, struct module *, void *), void *);
+static int lkm_fixfile_cb(const char *, void *, void *);
+static int lkm_fixsel(const char *, void *);
+static void lkm_emit_parents(struct module *, FILE *fp);
+static int lkm_emit_modulelist_cb(const char *, void *, void *);
+//static int lkm_emit_moduledefs_cb(const char *, void *, void *);
+static int lkm_make_files_cb(const char *, void *, void *);
+static int lkm_make_makefile(struct module *, const char *);
+static int lkm_make_lkmfile(struct module *, const char *);
+static void lkm_emit_loc(struct module *, FILE *);
+static int add_lkm_file_cb(const char *, void *, void *);
+static int lkm_emit_ioconf(struct module *, struct files *, FILE *);
+static int lkm_emit_ioconf_pseudo(struct module *, struct files *, FILE *, char **);
+struct emit_require_args {
+       FILE *fp;
+       struct module *cur_module;
+};
+static int lkm_emitrequire_cb(const char *, void *, void *);
+static int lkm_emitprovide_cb(const char *, void *, void *);
+
+static void lkm_emitsrcs(FILE *, void *);
+static void lkm_emitobjs(FILE *, void *);
+static void lkm_emitkmod(FILE *, void *);
+
+int merge_modules(struct module *, struct module *);
+
+#ifndef        STAILQ_APPEND
+#define        STAILQ_APPEND(q0, q1, field)    do {            \
+       if (!STAILQ_EMPTY(q1)) {                        \
+               *(q0)->stqh_last = STAILQ_FIRST(q1);    \
+               (q0)->stqh_last = (q1)->stqh_last;      \
+       }                                               \
+} while (/*CONSTCOND*/0)
+#endif
+
+
+int
+fixlkmdevs(void)
+{
+       struct devbase *d;
+       struct deva     *da;
+       struct devi     *i, *j;
+
+       TAILQ_FOREACH(d, &allbases, d_next) {
+
+               if (fixlkm_devbase(d))
+                       return -1;
+
+               for (da = d->d_ahead; da != NULL; da = da->d_bsame) {
+                       if (da->d_name == d->d_name) {
+                               /* the device doesn't have separate
+                                  attachment attribute. */
+                               continue;
+                       }
+
+                       if (fixlkm_deva(da))
+                               return -1;
+               }
+
+               for (i = d->d_ihead; i != NULL; i = i->i_bsame) {
+                       for (j=i; j != NULL; j = j->i_alias) {
+                               if (fixlkm_devi(j))
+                                       return -1;
+                       }
+               }
+       }
+
+       /* select new modules required by already selected modules */
+       ht_enumerate(moduletab, lkm_select_cb, lkm_select_attr);
+       /* and record dependencies between selected modules */
+       ht_enumerate(moduletab, lkm_select_cb, lkm_record_require);
+
+       return 0;
+}
+
+static int
+fixlkm_devbase(struct devbase *d)
+{
+       struct devi     *i, *j;
+       struct module   *m;
+       int d_module = 0;
+
+       /* if it's in the kernel, don't make an LKM for it */
+       if (ht_lookup(selecttab, d->d_name) != NULL)
+               return 0;
+
+       /* if device instance is in the module, we also need its base */
+       for (i = d->d_ihead; i != NULL; i = i->i_bsame)
+               for (j = i; j != NULL; j = j->i_alias) {
+                       if (j->i_active == DEVI_MODULE) {
+                               ++d_module;
+                               /* break; */
+                       }
+               }
+
+       if (d_module == 0)
+               return 0;
+
+       /* make a module for this devbase
+          the name of the module is device name */
+       m = getmodule(d->d_name,
+                     d->d_ispseudo ? M_PSEUDO: M_DEVICE);
+       if (m == NULL)
+               return -1;
+       STAILQ_INSERT_TAIL(&m->m_base, d, d_mnext);
+       d->d_module = m;
+
+       return 0;
+}
+
+
+/*
+ */
+static int
+fixlkm_deva(struct deva *a)
+{
+       struct devi     *i;
+       struct devbase  *base;
+       struct module   *m;
+       int a_module = 0;
+
+       /* if it's in the kernel, don't make LKM */
+       if (ht_lookup(selecttab, a->d_name) != NULL)
+               return 0;
+
+       /* if device instance is in the module, we also need its attachment */
+       for (i = a->d_ihead; i != NULL; i = i->i_asame) {
+               if (i->i_active == DEVI_MODULE) {
+                       ++a_module;
+               }
+       }
+
+       if (!a_module)
+               return 0;
+
+       /* this device attachment is compiled as LKM */
+
+       m = getmodule(a->d_name, M_DEVICE);
+       if (m == NULL)
+               return -1;
+
+       a->d_module = m;
+       STAILQ_INSERT_TAIL(&m->m_deva, a, d_mnext);
+
+       base = a->d_devbase;
+       if (base && base->d_module)
+               ht_insert(m->m_require, base->d_name, base->d_module);
+
+       return 0;
+}
+
+static int
+fixlkm_devi(struct devi *i)
+{
+       struct module *m = NULL;
+       struct devbase *b;
+       struct deva *a;
+       char *instname;
+
+       if (i->i_active != DEVI_MODULE)
+               return 0;
+
+       a = i->i_atdeva;
+       b = i->i_base;
+       if (a && a->d_module)
+               m = a->d_module;
+       else if (b && b->d_module)
+               m = b->d_module;
+
+
+       if (m) {
+               STAILQ_INSERT_TAIL(&m->m_devi, i, i_mnext);
+       }
+       else {
+               /* This instance has its base and attachment in the
+                * kernel */
+#if 0
+               /* make one module for each device instance */
+               m = getmodule(i->i_name, M_DEVICE);
+#else
+               /* pack those device instances into one module */
+               char devi_modname[100];
+
+               snprintf(devi_modname, sizeof devi_modname, "%s_",
+                        i->i_base->d_name);
+               m = getmodule(intern(devi_modname), M_DEVICE);
+#endif
+               if (m == NULL)
+                       return -1;
+
+               STAILQ_INSERT_TAIL(&m->m_devi, i, i_mnext);
+       }
+       i->i_module = m;
+
+       /* for lkm.provide */
+       if (m->m_type == M_DEVICE) {
+               if (i->i_at)
+                       asprintf(&instname, "%s@%s", i->i_name, i->i_at);
+               else
+                       instname = __UNCONST(i->i_name);
+               ht_insert(m->m_selecttab, instname, i);
+       }
+
+
+       return 0;
+}
+
+static struct module *
+newmodule(const char *name, enum module_type type)
+{
+       struct module *m;
+
+       m = ecalloc(1, sizeof *m);
+       m->m_name = name;
+       m->m_type = type;
+       STAILQ_INIT(&m->m_base);
+       STAILQ_INIT(&m->m_devi);
+       STAILQ_INIT(&m->m_deva);
+       STAILQ_INIT(&m->m_files);
+       m->m_selecttab = ht_new();
+       m->m_require = ht_new();
+       return m;
+}
+
+static struct module *
+getmodule(const char *name, enum module_type type)
+{
+       struct module *m = NULL;
+       static int anonymous_modules = 0;
+
+       if (name != NULL)
+               m = ht_lookup(moduletab, name);
+       else {
+               char *n;
+               asprintf(&n, "M%03d", anonymous_modules++);
+               name = n;
+       }
+
+       if (m == NULL) {
+               m = newmodule(name, type);
+               ht_insert(moduletab, name, m);
+               return m;
+       }
+
+       if (m->m_type != type) {
+               cfgerror("%s: duplicate module", name);
+               return (NULL);
+       }
+
+       return m;
+}
+
+
+static void
+lkm_select_attr(struct attr *a, void *context)
+{
+       struct module *cur_module = context;
+       struct module *m;
+
+       if (ht_lookup(selecttab, a->a_name)) {
+               /* it's in the kernel. skip  */
+               return;
+       }
+
+       /* We need this attribute for cur_module. */
+#if 0
+       printf("%s: %s -> %s\n", __FUNCTION__,
+              cur_module->m_name, a->a_name);
+#endif
+
+       m = ht_lookup(moduletab, a->a_name);
+       if (m == NULL) {
+               m = getmodule(a->a_name, M_ATTRIBUTE);
+               ht_insert(m->m_selecttab, a->a_name, __UNCONST(a->a_name));
+               m->u.m_attr = a;
+       }
+       else if (m == cur_module) {
+               /* no need to record dependency */
+               return;
+       }
+}
+
+static void
+lkm_record_require(struct attr *a, void *context)
+{
+       struct module *cur_module = context;
+
+       if (ht_lookup(selecttab, a->a_name)) {
+               /* it's in the kernel. skip  */
+               return;
+       }
+
+#if 0
+       printf("%s: %s -> %s\n", __FUNCTION__,
+              cur_module->m_name, a->a_name);
+#endif
+
+       ht_insert(cur_module->m_require, a->a_name, a);
+}
+
+
+static int
+/*ARGSUSED*/
+lkm_select_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+
+       switch (m->m_type) {
+       case M_DEVICE:
+       case M_PSEUDO:
+               return lkm_select_device(m, aux);
+       default:
+               if (m->u.m_attr)
+                       expandattr(m->u.m_attr, m, aux);
+       }
+
+       return 0;
+}
+
+
+
+static int
+lkm_select_device(struct module *m, void (* attr_func)(struct attr *, void *))
+{
+       struct devbase *d;
+       struct deva *da;
+       struct attr *a;
+       struct nvlist *nv;
+
+       STAILQ_FOREACH(d, &m->m_base, d_mnext) {
+               if (ht_lookup(selecttab, d->d_name) != NULL)
+                       continue;
+
+               ht_insert(m->m_selecttab, d->d_name, __UNCONST(d->d_name));
+               for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
+                       a = nv->nv_ptr;
+                       expandattr(a, m, attr_func);
+               }
+       }
+
+       STAILQ_FOREACH(da, &m->m_deva, d_mnext) {
+               if (ht_lookup(selecttab, da->d_name) != NULL)
+                       continue;
+               ht_insert(m->m_selecttab, da->d_name, __UNCONST(da->d_name));
+               for (nv = da->d_attrs; nv != NULL; nv = nv->nv_next) {
+                       a = nv->nv_ptr;
+                       expandattr(a, m, attr_func);
+               }
+       }
+       return 0;
+}
+
+static void
+fixlkmfiles2(struct filetype *ft, struct nvlist *expr)
+{
+       struct module *m;
+       struct nvlist *p;
+
+       if (ft->fit_nmods == 0) {
+               /* not for modules */
+       }
+       else if (ft->fit_nmods == 1) {
+               /* required by just one module. This file simply
+                * becomes a part of the modules. */
+               m = ft->fit_modules->nv_ptr;
+               STAILQ_INSERT_TAIL(&m->m_files, ft, fit_msame);
+
+               nvfree(ft->fit_modules);
+               ft->fit_modules = NULL;
+       }
+       else {
+               /* This file is required by multiple modules. */
+#if 1
+               /* Merge those modules that share this file into one module */
+               struct module *mmod;
+
+               mmod = getmodule(NULL, M_MERGE);
+               mmod->m_modules = NULL;
+               mmod->u.m_attr = NULL;
+
+               for (p = ft->fit_modules; p; p = p->nv_next) {
+                       m = p->nv_ptr;
+                       while (m->m_merged)
+                               m = m->m_merged;
+                       if (m == mmod)
+                               continue;
+
+                       mmod->m_modules =
+                               newnv(m->m_name, NULL, m, 0, mmod->m_modules);
+                       m->m_merged = mmod;
+               }
+
+               /* add this file to the merged module */
+               STAILQ_INSERT_TAIL(&mmod->m_files, ft, fit_msame);
+               ft->fit_flags |= FI_MODULE;
+#else
+               /* This is an another way to handle files shared by
+                  multiple modules: Create a new module to have
+                  shared files. */
+
+               char *expr_str = expr_canonstr(expr);
+               struct module *fg, *m;
+
+               /* make a group of files which are turned on/off by
+                * the same expression. */
+
+               fg = ht_lookup(moduletab, expr_str);
+               if (fg) {
+                       free(expr_str);
+                       assert(fg->m_type == M_FILEGROUP);
+               }
+               else {
+                       /* create a new filegroup */
+                       fg = newmodule(expr_str, M_FILEGROUP);
+                       assert(fg != NULL);
+                       ht_insert(moduletab, expr_str, fg);
+               }
+
+               /* add this file to the filegroup */
+               STAILQ_INSERT_TAIL(&fg->m_files, ft, fit_msame);
+               ft->fit_flags |= FI_MODULE;
+
+               for (p = ft->fit_modules; p; p = p->nv_next) {
+                       m = p->nv_ptr;
+                       ht_insert(m->m_require, fg->m_name, fg);
+               }
+#endif
+       }
+}
+
+#if 0
+static int
+/*ARGSUSED*/
+fix_filegroup_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       char buf[MAXPATHLEN];
+       struct filetype *ft;
+
+       if (m->m_type != M_FILEGROUP)
+               return 0;
+
+       /* currently, this module has a name generated from an
+        * expression.  rename it to a normal module name based on the
+        * files included in this filegroup */
+
+       buf[0] = '\0';
+
+       STAILQ_FOREACH(ft, &m->m_files, fit_msame) {
+               /* XXX .o */
+               struct files *fi = (struct files *)ft;
+
+               /* XXX: I know you can optimize this...*/
+               strlcat(buf, "_", sizeof buf);
+               strlcat(buf, fi->fi_base, sizeof buf);
+       }
+
+       m->m_name = estrdup(buf);
+
+       /* Now this module has a new name, but the key in moduletab is
+        * unchanged.
+        */
+       return 0;
+}
+#endif
+
+static int
+/*ARGSUSED*/
+mark_empty_attr_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+
+       if (m->m_merged == NULL &&
+           m->m_type == M_ATTRIBUTE && m->u.m_attr != NULL &&
+           STAILQ_EMPTY(&m->m_files)) {
+#if 0
+               printf("%s is empty\n", m->u.m_attr->a_name);
+#endif
+               ht_insert(emptyattrtab, m->u.m_attr->a_name,
+                         m->u.m_attr);
+       }
+       return 0;
+}
+
+static int
+/*ARGSUSED*/
+fix_merged_cb(const char *key, void *value, void *aux)
+{
+       struct nvlist *p;
+       struct module *m = (struct module *)value;
+       struct module *devmod = NULL;
+       int device = 0;
+
+       if (m->m_type != M_MERGE)
+               return 0;
+
+       if (m->m_merged != NULL)
+               return 0;
+
+       for (p=m->m_modules; p; p = p->nv_next) {
+               struct module *n = p->nv_ptr;
+               if (n->m_type == M_DEVICE) {
+                       ++device;
+                       devmod = n;
+               }
+               else if (n->m_type == M_MERGE) {
+                       struct nvlist *q0, *q1;
+                       /* insert module list from *n */
+                       q0 = p->nv_next;
+                       p->nv_next = q1 = n->m_modules;
+                       while (q1->nv_next)
+                               q1 = q1->nv_next;
+                       q1->nv_next = q0;
+               }
+
+               merge_modules(m, n);
+       }
+
+       /* name the merged module */
+       /* if there is only one device in the merged module, use the
+        * device name for the module. */
+       if (device == 1) {
+               m->m_name = devmod->m_name;
+       }
+       /* if the merged module has only one file, use it for
+        * the module's name */
+       else if (!STAILQ_EMPTY(&m->m_files) &&
+                STAILQ_NEXT(STAILQ_FIRST(&m->m_files), fit_msame) == NULL) {
+               /* XXX */
+               struct files *f = (struct files *)STAILQ_FIRST(&m->m_files);
+               m->m_name = f->fi_base;
+       }
+       else {
+               /* Otherwise, keep autogenerated name such as "M001".
+                  FIXME: Maybe better to use the first file name?
+                         or, append all file names?
+               */
+       }
+
+       if (device)
+               m->m_type = M_DEVICE;
+       else
+               m->m_type = M_ATTRIBUTE;
+
+       return 0;
+}
+
+/*
+ * Check which file is needed by which modules.
+ */
+int
+fixlkmfiles(void)
+{
+       struct files *fi;
+
+       if (ht_enumerate(moduletab, lkm_fixfile_cb, NULL))
+               return -1;
+
+       TAILQ_FOREACH(fi, &allfiles, fi_next) {
+               fixlkmfiles2(&fi->fi_fit, fi->fi_optx);
+       }
+
+#if 0
+       /* fix filegroup modules */
+       if (ht_enumerate(moduletab, fix_filegroup_cb, NULL))
+               return -1;
+#endif
+
+       if (ht_enumerate(moduletab, fix_merged_cb, NULL))
+               return -1;
+
+       if (ht_enumerate(moduletab, mark_empty_attr_cb, NULL))
+               return -1;
+
+       if (ht_enumerate(moduletab, add_lkm_file_cb, NULL))
+               return -1;
+
+       return 0;
+}
+
+static void
+add_module_for_file(struct filetype *ft, struct module *m)
+{
+       ft->fit_modules = newnv(m->m_name, NULL, m, 0, ft->fit_modules);
+       ft->fit_nmods++;
+}
+
+static int
+/*ARGSUSED*/
+lkm_fixfile_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       struct files *fi;
+       struct objects *oi;
+
+       TAILQ_FOREACH(fi, &allfiles, fi_next) {
+               if (fi->fi_flags & (FI_HIDDEN|FI_SEL)) {
+                       /* if the files is in the kernel, skip it */
+                       continue;
+               }
+
+               if (expr_eval(fi->fi_optx, lkm_fixsel, m)) {
+                       /* Mark this file is required by modules */
+
+                       add_module_for_file(&fi->fi_fit, m);
+               }
+       }
+
+       TAILQ_FOREACH(oi, &allobjects, oi_next) {
+               if (oi->oi_flags & (FI_HIDDEN|FI_SEL)) {
+                       /* if the files is in the kernel, skip it */
+                       continue;
+               }
+               if (oi->oi_flags & FI_MODULE) {
+                       /* We already know about this file */
+                       continue;
+               }
+               if (expr_eval(oi->oi_optx, lkm_fixsel, m)) {
+                       /* Mark this file is required by modules */
+                       oi->oi_flags |= FI_MODULE;
+
+                       STAILQ_INSERT_HEAD(&m->m_files, &oi->oi_fit, fit_msame);
+               }
+       }
+
+       return 0;
+}
+
+static int
+lkm_fixsel(const char *name, void *context)
+{
+       struct module *m = context;
+       return ht_lookup(selecttab, name) != NULL ||
+               ht_lookup(m->m_selecttab, name) != NULL;
+}
+
+static int
+/*ARGSUSED*/
+add_lkm_file_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       struct files *fi;
+       char *pathname;
+
+       assert(m->m_lkmfile == NULL);
+
+       if (m->m_merged != NULL) {
+               /* This module has been merged into the other module. */
+               return 0;
+       }
+
+       switch (m->m_type) {
+       case M_DEVICE:
+               if (STAILQ_EMPTY(&m->m_files)) {
+                       if (!STAILQ_EMPTY(&m->m_devi)) {
+                               /* devi only module */
+                       }
+                       else if (!STAILQ_EMPTY(&m->m_base)) {
+                               /* config staff only */
+                       }
+                       else {
+                               /* we won't create this module if this
+                                * module is empty. */
+                               return 0;
+                       }
+               }
+               break;
+       case M_ATTRIBUTE:
+       default:
+               if (STAILQ_EMPTY(&m->m_files))
+                       return 0;
+       }
+
+       /* add _MODULE_lkm.c */
+       asprintf(&pathname, "%s/%s/%s%s/_%s_lkm.c",
+                builddir, KMODDIR,
+                m->m_name, KMODWORK_SUFFIX, m->m_name);
+
+       fi = addfile(pathname, NULL, FI_MODULE, NULL);
+       if (fi == NULL) {
+               cfgerror("can't create file %s", pathname);
+               return -1;
+       }
+
+       m->m_lkmfile = fi;
+
+       return 0;
+}
+
+/*
+ * Try to reduce the number of LKMs by packing many modules into one.
+ * This routine is called if option -G is given.
+ */
+int
+packlkms(void)
+{
+       /* not yet */
+       return 0;
+}
+
+/*
+ * merge module m1 into module m0
+ */
+int
+merge_modules(struct module *m0, struct module *m1)
+{
+       if (m1->m_type == M_DEVICE) {
+
+               STAILQ_APPEND(&m0->m_base, &m1->m_base, d_mnext);
+               STAILQ_INIT(&m1->m_base);
+               STAILQ_APPEND(&m0->m_deva, &m1->m_deva, d_mnext);
+               STAILQ_INIT(&m1->m_deva);
+               STAILQ_APPEND(&m0->m_devi, &m1->m_devi, i_mnext);
+               STAILQ_INIT(&m1->m_devi);
+       }
+
+       ht_concat(m0->m_selecttab, m1->m_selecttab, 0);
+       ht_concat(m0->m_require, m1->m_require, 0);
+
+       /* append file list */
+       STAILQ_APPEND(&m0->m_files, &m1->m_files, fit_msame);
+       STAILQ_INIT(&m1->m_files);
+
+       m1->m_merged = m0;
+       return 0;
+}
+
+/*
+ * check device major numbers for LKMs
+ */
+int
+fixlkmdevsw(void)
+{
+       int er = 0;
+
+       /* XXX: CodeMe! */
+
+       return er;
+}
+
+/*
+ *
+ */
+int
+mklkms(void)
+{
+       char moddir[MAXPATHLEN];
+
+       if (ht_isempty(moduletab)) {
+               return 0;
+       }
+
+       snprintf(moddir, sizeof moddir, "%s/%s", builddir, KMODDIR);
+       make_directory(moddir);
+       return ht_enumerate(moduletab, lkm_make_files_cb, NULL);
+}
+
+static int
+/*ARGSUSED*/
+lkm_make_files_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = value;
+       char moddir[MAXPATHLEN];
+
+       if (m->m_lkmfile == NULL)
+               return 0;
+
+       snprintf(moddir, sizeof moddir, "%s/%s/%s%s",
+                builddir, KMODDIR, m->m_name, KMODWORK_SUFFIX);
+       make_directory(moddir);
+
+       return lkm_make_makefile(m, moddir) || lkm_make_lkmfile(m, moddir);
+}
+
+static int
+lkm_make_makefile(struct module *m, const char *moddir)
+{
+       static const struct makefile_constructs c[] = {
+               { "OBJS", lkm_emitobjs },
+               { "SRCS", lkm_emitsrcs },
+               { "KMOD", lkm_emitkmod },
+               { NULL, NULL }
+       };
+
+       return mkmakefile_common(c, ".lkm", moddir, m);
+}
+
+static void
+lkm_lkmfile_header(struct module *m, struct files *f,
+                  const char *conffile, FILE *fp)
+{
+       fprintf(fp, "/*\n"
+               " * MACHINE GENERATED: DO NOT EDIT\n"
+               " *\n"
+               " * %s, from \"%s\"\n"
+               " */\n\n"
+               "#include <sys/param.h>\n"
+               "#include <sys/conf.h>\n"
+               "#include <sys/device.h>\n"
+               "#include <sys/mount.h>\n"
+               "#include <sys/lkm.h>\n"
+               "\n\n",
+
+               f->fi_base, conffile);
+}
+
+
+static void
+lkm_devsw(struct module *m, FILE *fp, char **entry_func_name)
+{
+       struct devbase *d;
+       struct devm *dm;
+       int bmaj, cmaj;
+       int count = 0;
+       char bdevbuf[30], cdevbuf[30];
+       char *bdev, *cdev;
+
+       STAILQ_FOREACH(d, &m->m_base, d_mnext) {
+               bmaj = cmaj = -1;
+
+               dm = ht_lookup(bdevmtab, d->d_name);
+               if (dm && dm->dm_active != DEVM_INKERNEL)
+                       bmaj = dm->dm_bmajor;
+
+               dm = ht_lookup(cdevmtab, d->d_name);
+               if (dm && dm->dm_active != DEVM_INKERNEL)
+                       cmaj = dm->dm_cmajor;
+
+               if (bmaj < 0 && cmaj < 0)
+                       continue;
+
+               if (count++ <= 0) {
+                       asprintf(entry_func_name, "%s_devsw", dm->dm_name);
+                       fprintf(fp,
+                               "\nstatic int\n"
+                               "%s(struct lkm_table *lkmtp, int cmd)\n"
+                               "{\n"
+                               "\tint b, c, er;\n",
+                               *entry_func_name);
+               }
+
+               if (cmaj < 0)
+                       cdev = "NULL";
+               else {
+                       snprintf(cdevbuf, sizeof cdevbuf, "&%s_cdevsw",
+                                d->d_name);
+                       fprintf(fp, "\textern struct cdevsw %s;\n", cdevbuf+1);
+                       cdev = cdevbuf;
+               }
+
+               if (bmaj < 0)
+                       bdev = "NULL";
+               else {
+                       snprintf(bdevbuf, sizeof bdevbuf, "&%s_bdevsw",
+                                d->d_name);
+                       fprintf(fp, "\textern struct bdevsw %s;\n", bdevbuf+1);
+                       bdev = bdevbuf;
+               }
+
+               fprintf(fp, "\tswitch(cmd) {\n\tcase LKM_E_LOAD:\n");
+
+               fprintf(fp,
+                       "\t\tb = %d; c = %d;\n"
+                       "\t\ter = devsw_attach(\"%s\", %s, &b, %s, &c);\n"
+                       "\t\tif (er) return er;\n"
+                       "\t\tbreak;\n"
+                       "\tcase LKM_E_UNLOAD:\n"
+                       "\t\tdevsw_detach(%s, %s);\n"
+                       "\t\tbreak;\n"
+                       "\t}\n",
+                       bmaj, cmaj, d->d_name, bdev, cdev,
+                       bdev, cdev);
+       }
+
+       if (count > 0) {
+               fprintf(fp, "\n\treturn 0;\n}\n\n");
+       }
+
+}
+
+static int
+lkm_make_lkmfile(struct module *m, const char *moddir)
+{
+       FILE *fp;
+       char tmpname[MAXPATHLEN];
+       struct files *f = m->m_lkmfile;
+       char *entry_func = "lkm_nofunc";
+       struct emit_require_args args;
+
+       if (f == NULL)
+               return 0;
+
+#if 0
+       if (m->m_type == M_DEVICE &&
+           (STAILQ_EMPTY(&m->m_devi) && STAILQ_EMPTY(&m->m_base)) )
+               return 0;
+#endif
+
+       snprintf(tmpname, sizeof tmpname, "%s/tmp_lkm.c", moddir);
+
+       if ((fp = fopen(tmpname, "w")) == NULL) {
+               warn("cannot write %s", tmpname);
+               return 1;
+       }
+
+       switch (m->m_type) {
+       case M_DEVICE:
+               lkm_emit_ioconf(m, f, fp);
+               lkm_devsw(m, fp, &entry_func);
+               break;
+       case M_PSEUDO:
+               lkm_emit_ioconf_pseudo(m, f, fp, &entry_func);
+               break;
+       default:
+               lkm_lkmfile_header(m, f, conffile, fp);
+
+               fprintf(fp, "MOD_MISC(\"%s\");\n", m->m_name);
+       }
+
+       fprintf(fp,
+               "int __%s_lkmentry(struct lkm_table*, int, int);\n"
+               "int\n"
+               "__%s_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)\n"
+               "{ DISPATCH(lkmtp, cmd, ver, %s, lkm_nofunc, lkm_nofunc); }\n"
+               "__weak_alias(%s_lkmentry, __%s_lkmentry);\n",
+
+               m->m_name,
+               m->m_name,
+               entry_func,
+               m->m_name, m->m_name);
+
+       /* emit required module list */
+       fprintf(fp,
+               "\n\nasm(\"     .section \\\".note.netbsd.lkm.require\\\",\\\"\\\"\\n\"\n");
+       args.fp = fp;
+       args.cur_module = m;
+       ht_enumerate(m->m_require, lkm_emitrequire_cb, &args);
+       fprintf(fp, "    \"     .byte 0\\n\");\n");
+
+       /* emit provided attributes */
+       fprintf(fp,
+               "\n\nasm(\"     .section \\\".note.netbsd.lkm.provide\\\",\\\"\\\"\\n\"\n");
+       ht_enumerate(m->m_selecttab, lkm_emitprovide_cb, fp);
+       fprintf(fp, "    \"     .byte 0\\n\");\n");
+
+       fclose(fp);
+
+       return (moveifchanged(tmpname, f->fi_path));
+}
+
+static int
+lkm_emit_ioconf(struct module *m, struct files *f, FILE *fp)
+{
+       char cfdataname[80];
+       struct devi *i;
+       struct devbase *d = NULL;
+       struct deva *da;
+       struct devi *di;
+       struct nvlist *att_list = NULL;
+       struct nvlist *nv;
+
+       ioconf_emithdr(fp, f->fi_tail);
+
+       fprintf(fp, "#include <sys/lkm.h>\n\n");
+
+       STAILQ_FOREACH(d, &m->m_base, d_mnext) {
+               /* This module has the devbase */
+               ioconf_emit_ref_iattrs(d, fp);
+               ioconf_emit_cfdriver(d, fp);
+
+               for (da = d->d_ahead; da != NULL; da = da->d_bsame) {
+                       if (da->d_name == d->d_name) {
+                               /* the devbase is also an attachment */
+                               att_list = newnv(da->d_name, NULL, da, 0,
+                                                att_list);
+
+                               fprintf(fp, "extern struct cfattach %s_ca;\n",
+                                       da->d_name);
+                       }
+               }
+       }
+
+       STAILQ_FOREACH(da, &m->m_deva, d_mnext) {
+               att_list = newnv(da->d_name, NULL, da, 0, att_list);
+       }
+       fprintf(fp, "\n\n");
+
+       if (!STAILQ_EMPTY(&m->m_devi)) {
+               lkm_emit_parents(m, fp);
+               fprintf(fp, "\n");
+               lkm_emit_loc(m, fp);
+               fprintf(fp, "\n");
+       }
+
+       snprintf(cfdataname, sizeof cfdataname, "%s_cfdata", m->m_name);
+       ioconf_cfdata_start(fp, cfdataname);
+
+       STAILQ_FOREACH(i, &m->m_devi, i_mnext) {
+               ioconf_cfdata_entry(fp, i);
+       }
+
+       ioconf_cfdata_end(fp, cfdataname);
+
+       fprintf(fp, "static struct cfdriver *%s_cfdrivers[] = {\n",
+               m->m_name);
+       STAILQ_FOREACH(d, &m->m_base, d_mnext)
+               fprintf(fp, "\t&%s_cd,\n", d->d_name);
+       fprintf(fp, "\tNULL\n};\n");
+
+       if (att_list != NULL) {
+               for (nv = att_list; nv; nv = nv->nv_next) {
+                       fprintf(fp, "extern struct cfattach %s_ca;\n", nv->nv_name);
+               }
+               fprintf(fp, "static struct cfattach *%s_cfattachs[] = {\n",
+                       m->m_name);
+               for (nv = att_list; nv; nv = nv->nv_next) {
+                       fprintf(fp, "\t&%s_ca,\n", nv->nv_name);
+               }
+               fprintf(fp, "\tNULL\n};\n");
+       }
+
+       /* We want devbase name for this module */
+       d = STAILQ_FIRST(&m->m_base);
+       if (d == NULL) {
+               da = STAILQ_FIRST(&m->m_deva);
+               if (da)
+                       d = da->d_devbase;
+               if (d == NULL) {
+                       di = STAILQ_FIRST(&m->m_devi);
+                       if (di)
+                               d = di->i_base;
+               }
+       }
+
+       if (d == NULL) {
+               cfgerror("can't get device base name for module %s",
+                        m->m_name);
+       }
+       else {
+               fprintf(fp,
+                       "static const struct cfattachlkminit %s_cfattachinit[] = {\n",
+                       m->m_name);
+               if (att_list != NULL) {
+                       fprintf(fp,
+                               "\t{\"%s\", %s_cfattachs},\n",
+                               d->d_name,  m->m_name);
+               }
+               fprintf(fp,
+                       "\t{NULL, NULL}\n"
+                       "};\n");
+       }
+
+       fprintf(fp,
+               "int __%s_lkmentry(struct lkm_table*, int, int);\n"
+               "MOD_DRV(\"%s\", %s_cfdrivers, %s_cfattachinit, %s_cfdata);\n",
+               m->m_name,
+               m->m_name, m->m_name, m->m_name, m->m_name);
+
+       if (att_list)
+               nvfreel(att_list);
+
+       return 0;
+}
+
+static int
+lkm_emit_ioconf_pseudo(struct module *m, struct files *f, FILE *fp, char **entry_func_ret)
+{
+       char cfdataname[80];
+       struct devbase *d = NULL;
+
+       ioconf_emithdr(fp, f->fi_tail);
+
+       fprintf(fp, "#include <sys/lkm.h>\n\n");
+
+       d = STAILQ_FIRST(&m->m_base);
+       assert(d != NULL);
+
+       snprintf(cfdataname, sizeof cfdataname, "%s_cfdata", m->m_name);
+
+       if (devbase_has_instances(d, WILD)) {
+               ioconf_emit_ref_iattrs(d, fp);
+               ioconf_emit_cfdriver(d, fp);
+
+               fprintf(fp, "static struct cfdriver *%s_cfdrivers[] = {\n",
+                       m->m_name);
+               fprintf(fp, "\t&%s_cd,\n", d->d_name);
+               fprintf(fp, "\tNULL\n};\n");
+
+               ioconf_cfdata_start(fp, cfdataname);
+               ioconf_cfdata_end(fp, cfdataname);
+
+               fprintf(fp,
+                       "static const struct cfattachlkminit %s_cfattachinit[] = {\n",
+                       d->d_name);
+               fprintf(fp,
+                       "\t{NULL, NULL}\n"
+                       "};\n");
+
+               fprintf(fp,
+                       "MOD_DRV(\"%s\", %s_cfdrivers, %s_cfattachinit, %s_cfdata);\n",
+                       d->d_name, d->d_name, d->d_name, d->d_name);
+       }
+       else {
+               fprintf(fp,
+                       "MOD_MISC(\"%s\");\n",
+                       d->d_name);
+       }
+
+       fprintf(fp,
+               "static int __%s_load(struct lkm_table *lkmtp, int cmd)\n"
+               "{\n"
+               "\textern void %sattach(int);\n"
+               "\t%sattach(%d);\n"
+               "\treturn 0;\n"
+               "}\n\n",
+               d->d_name,
+               d->d_name, d->d_name, d->d_umax);
+
+       if (entry_func_ret) {
+               asprintf(entry_func_ret, "__%s_load", d->d_name);
+       }
+
+       return 0;
+}
+
+
+static void
+lkm_emit_parents(struct module *m, FILE *fp)
+{
+       struct devi *i;
+       struct nvlist *nv, *list = NULL;
+       struct pspec *ps;
+
+
+       STAILQ_FOREACH(i, &m->m_devi, i_mnext) {
+               ps = i->i_pspec;
+               if (ps == NULL)
+                       continue;
+               /* Check if we already emit this pspec */
+
+               for (nv = list; nv; nv = nv->nv_next)
+                       if (nv->nv_ptr == ps)
+                               break;
+
+               if (nv)
+                       continue;
+
+               ioconf_emit_pspec(fp, ps, 1);
+
+               /* remember the pspec */
+               list = newnv("", NULL, ps, 0, list);
+       }
+
+       if (list)
+               nvfreel(list);
+}
+
+static void
+lkm_emit_loc(struct module *m, FILE *fp)
+{
+       struct pspec *ps;
+       struct devi *i;
+       int l, k;
+       int n = 0;
+
+       fprintf(fp, "\nstatic int loc[] = {");
+
+       STAILQ_FOREACH(i, &m->m_devi, i_mnext) {
+               if ((ps = i->i_pspec) != NULL &&
+                   (l = ps->p_iattr->a_loclen) > 0) {
+
+                       if (n == 0)
+                               fprintf(fp, "\n");
+                       fprintf(fp, "\t/* %d */\t", n);
+                       for (k = 0; k < l; ++k) {
+                               fprintf(fp, "%s, ", i->i_locs[k]);
+                       }
+                       fprintf(fp, "\n");
+
+                       i->i_locoff = n;
+                       n += l;
+               }
+       }
+
+       if (n > 0) {
+               fprintf(fp, "};  /* %d */\n\n", n);
+       }
+       else {
+               fprintf(fp, " -1 };\n");
+       }
+}
+
+void
+/*ARGSUSED*/
+emitkmod(FILE *fp, void *aux)
+{
+       if (ht_isempty(moduletab)) {
+               fprintf(fp, "# No LKMs\n");
+               return;
+       }
+
+       fprintf(fp, "MODULES= \\\n");
+       ht_enumerate(moduletab, lkm_emit_modulelist_cb, fp);
+       fprintf(fp, "\n");
+
+#if 0
+       ht_enumerate(moduletab, lkm_emit_moduledefs_cb, fp);
+
+       fprintf(fp,
+               "\n"
+               ".if exists(\"$S/arch/%s/conf/Makefile.kmod.inc.%s\")\n"
+               ".include \"$S/arch/%s/conf/Makefile.kmod.inc.%s\"\n"
+               ".elif exists(\"$S/arch/%s/conf/Makefile.kmod.inc.%s\")\n"
+               ".include \"$S/arch/%s/conf/Makefile.kmod.inc.%s\"\n"
+               ".else\n"
+               ".include \"$S/conf/Makefile.kmod.inc\"\n"
+               ".endif\n",
+               machine, machine, machine, machine,
+               machinearch, machinearch, machinearch, machinearch);
+#endif
+}
+
+static int
+/*ARGSUSED*/
+lkm_emit_modulelist_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       FILE *fp = aux;
+
+       if (m->m_merged != NULL)
+               return 0;
+
+       if (m_empty(m))
+               return 0;
+
+       fprintf(fp, "\t%s \\\n", m->m_name);
+
+       return 1;
+}
+
+#if 0
+static int
+/*ARGSUSED*/
+lkm_emit_moduledefs_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       FILE *fp = aux;
+       struct filetype *f;
+       int has_o = 0;
+
+       if (m_empty(m)) {
+               fprintf(fp, "#MODULE.%s: no files for module %s\n",
+                       m->m_name, m->m_name);
+               return 0;
+       }
+
+       fprintf(fp, "MODULE.%s.SRCS=\t", m->m_name);
+
+       STAILQ_FOREACH(f, &m->m_files, fit_msame) {
+               if (f->fit_path[0] == '/')
+                       fprintf(fp, "%s ", f->fit_path);
+               else
+                       fprintf(fp, "${S}/%s ", f->fit_path);
+
+               if (f->fit_lastc == 'o')
+                       ++has_o;
+       }
+       if (m->m_lkmfile)
+               fprintf(fp, "%s", m->m_lkmfile->fi_path);
+       fprintf(fp, "\n");
+
+       if (!has_o)
+               return 0;
+
+       STAILQ_FOREACH(f, &m->m_files, fit_msame) {
+               if (f->fit_lastc != 'o')
+                       continue;
+               fprintf(fp, "OBJFILE_%s_MODULE=%s\n",
+                       f->fit_path, m->m_name);
+       }
+
+       return 0;
+}
+#endif
+
+static void
+lkm_emitkmod(FILE *fp, void *aux)
+{
+       struct module *m = aux;
+
+       /* .LKM is appended to module name to avoid conflict in case
+          we have MODULE.c */
+       fprintf(fp, "KMOD=\t%s\n", m->m_name);
+}
+
+static void
+lkm_emitobjs(FILE *fp, void *aux)
+{
+       struct module *m = aux;
+       struct filetype *ft;
+       const char *sep;
+
+       if (m_empty(m)) {
+               fprintf(fp, "# no files for module %s\n",
+                       m->m_name);
+               return;
+       }
+
+       fprintf(fp, "OBJS=\t");
+       sep = "";
+       STAILQ_FOREACH(ft, &m->m_files, fit_msame) {
+               if (ft->fit_lastc != 'o')
+                       continue;
+               fprintf(fp, "%s%s", sep, ft->fit_path);
+               sep = "\t\\\n\t";
+       }
+       fprintf(fp, "\n");
+}
+
+static int
+lkm_emitpath_cb(const char *key, void *value, void *aux)
+{
+       FILE *fp = aux;
+       if (*key == '/')
+               fprintf(fp, "%s ", key);
+       else
+               fprintf(fp, "${S}/%s ", key);
+       return 0;
+}
+
+static void
+lkm_emitsrcs(FILE *fp, void *aux)
+{
+       struct module *m = aux;
+       struct filetype *ft;
+       struct files *f;
+       const char *sep;
+       struct  hashtab *ht;
+
+       if (m_empty(m)) {
+               fprintf(fp, "# no files for module %s\n",
+                       m->m_name);
+               return;
+       }
+
+       /* set .PATH for sources */
+       ht = ht_new();
+       STAILQ_FOREACH(ft, &m->m_files, fit_msame) {
+               f = (struct files *)ft;
+               char buf[MAXPATHLEN];
+
+               if (ft->fit_lastc == 'o')
+                       continue;
+
+               if (f->fi_path == f->fi_tail) {
+                       /* No directory part */
+                       continue;
+               }
+               strlcpy(buf, f->fi_path,
+                       MIN(sizeof buf, f->fi_tail - f->fi_path));
+               ht_insert(ht, intern(buf), f);
+       }
+       if (!ht_isempty(ht)) {
+               fprintf(fp, ".PATH: ");
+               ht_enumerate(ht, lkm_emitpath_cb, fp);
+               fprintf(fp, "\n");
+       }
+       ht_free(ht);
+
+       fprintf(fp, "SRCS=\t");
+       sep = "";
+       STAILQ_FOREACH(ft, &m->m_files, fit_msame) {
+               if (ft->fit_lastc == 'o')
+                       continue;
+               f = (struct files *)ft;
+               fprintf(fp, "%s%s", sep, f->fi_tail);
+               sep = "\t\\\n\t";
+       }
+
+       if (m->m_lkmfile)
+               fprintf(fp, "%s%s", sep, m->m_lkmfile->fi_tail);
+       fprintf(fp, "\n");
+}
+
+
+
+static int
+/*ARGSUSED*/
+lkm_emitrequire_cb(const char *key, void *value, void *aux)
+{
+       struct emit_require_args *args = aux;
+       struct module *m = value;
+
+#if 0
+       printf("%s: %s -> %s\n", __FUNCTION__, args->cur_module->m_name, m->m_name);
+#endif
+
+       if (!ht_lookup(emptyattrtab, m->m_name) &&
+           !ht_lookup(args->cur_module->m_selecttab, m->m_name))
+               fprintf(args->fp, "    \"\t.asciz \\\"%s\\\"\\n\"\n", m->m_name);
+       return 0;
+}
+
+static int
+/*ARGSUSED*/
+lkm_emitprovide_cb(const char *key, void *value, void *aux)
+{
+       FILE *fp = aux;
+
+       fprintf(fp, "    \"\t.asciz \\\"%s\\\"\\n\"\n", key);
+       return 0;
+}
+
+
+#if 0
+static int
+for_all_module_files(struct module *m,
+                    int (* fn)(struct files *, struct module *, void *),
+                    void *arg)
+{
+       struct files *fi;
+       struct module *m2;
+
+       TAILQ_FOREACH(fi, &allfiles, fi_next) {
+               if (STAILQ_EMPTY(&fi->fi_modules))
+                       continue;
+               STAILQ_FOREACH(m2, &fi->fi_modules, m_filenext) {
+                       if (m2 == m) {
+                               if (fn(fi, m, arg) != 0)
+                                       return 1;
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+#endif
+
+static int
+/*ARGSUSED*/
+dump_module_cb2(const char *key, void *value, void *aux)
+{
+       printf("%s=%p ", key, value);
+       return 0;
+}
+
+static int
+/*ARGSUSED*/
+dump_module_cb(const char *key, void *value, void *aux)
+{
+       struct module *m = (struct module *)value;
+       struct devbase *d;
+       struct devi *i;
+       struct deva *a;
+       struct filetype *f;
+
+       printf("module %p %s type=%d\n", m, m->m_name, m->m_type);
+       printf("  base=");
+       STAILQ_FOREACH(d, &m->m_base, d_mnext)
+               printf("%p(%s) ", d, d->d_name);
+       printf("  deva=");
+       STAILQ_FOREACH(a, &m->m_deva, d_mnext) {
+               printf("%p(%s) ", a, a->d_name);
+       }
+       printf(" devis=");
+       STAILQ_FOREACH(i, &m->m_devi, i_mnext) {
+               printf("%p(%s) ", i, i->i_name);
+       }
+       printf("\n selections: ");
+
+       ht_enumerate(m->m_selecttab, dump_module_cb2, NULL);
+       printf("\n files: ");
+
+       STAILQ_FOREACH(f, &m->m_files, fit_msame) {
+               printf("%s ", f->fit_path);
+       }
+       printf("\n requires: ");
+
+       ht_enumerate(m->m_require, dump_module_cb2, NULL);
+       printf("\n");
+       return 0;
+}
+
+void
+dump_module_devis(void)
+{
+       struct devi *i;
+
+       printf("================\n");
+       TAILQ_FOREACH(i, &alldevi, i_next) {
+               if (i->i_module || i->i_active == DEVI_MODULE) {
+                       printf("devi %p %s i_module=%p i_active=%d\n",
+                              i, i->i_name, i->i_module, i->i_active);
+               }
+               else {
+                       printf("devi %s is not module\n", i->i_name);
+               }
+       }
+
+       ht_enumerate(moduletab, dump_module_cb, NULL);
+}
Index: hash.c
===================================================================
--- hash.c      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ hash.c      (.../tags/patch-1-rc1)  (revision 51)
@@ -315,3 +315,37 @@
       }
       return rval;
}
+
+/*
+ * return 1 if the hash table is empty.
+ */
+int
+ht_isempty(struct hashtab *ht)
+{
+       return ht->ht_used == 0;
+}
+
+/*
+ * concat two hash tables
+ */
+struct concat_arg {
+       struct hashtab *ht;
+       int replace;
+};
+
+static int
+concat_cb(const char *name, void *value, void *arg)
+{
+       struct concat_arg *a = arg;
+
+       return ht_insrep(a->ht, name, value, a->replace);
+}
+
+int
+ht_concat(struct hashtab *ht0, struct hashtab *ht1, int replace)
+{
+       struct concat_arg a;
+       a.replace = replace;
+       a.ht = ht0;
+       return ht_enumerate(ht1, concat_cb, &a);
+}
Index: main.c
===================================================================
--- main.c      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ main.c      (.../tags/patch-1-rc1)  (revision 51)
@@ -82,6 +82,7 @@
int    vflag;                          /* verbose output */
int    Pflag;                          /* pack locators */
int    Lflag;                          /* lint config generation */
+int    Gflag;                          /* reduce the number of LKMs */

int    yyparse(void);

@@ -110,7 +111,6 @@
static int     mksymlinks(void);
static int     mkident(void);
static int     devbase_has_dead_instances(const char *, void *, void *);
-static int     devbase_has_any_instance(struct devbase *, int, int, int);
static int     check_dead_devi(const char *, void *, void *);
static void    kill_orphans(void);
static void    do_kill_orphans(struct devbase *, struct attr *,
@@ -120,7 +120,6 @@
static const char *strtolower(const char *);
void   defopt(struct hashtab *ht, const char *fname,
            struct nvlist *opts, struct nvlist *deps, int obs);
-
#define LOGCONFIG_LARGE "INCLUDE_CONFIG_FILE"
#define LOGCONFIG_SMALL "INCLUDE_JUST_CONFIG"

@@ -147,7 +146,7 @@

       pflag = 0;
       xflag = 0;
-       while ((ch = getopt(argc, argv, "DLPgpvb:s:x")) != -1) {
+       while ((ch = getopt(argc, argv, "DGLPgpvb:s:x")) != -1) {
               switch (ch) {

#ifndef MAKE_BOOTSTRAP
@@ -156,6 +155,9 @@
                       break;
#endif

+               case 'G':
+                       Gflag = 1;
+                       break;
               case 'L':
                       Lflag = 1;
                       break;
@@ -280,6 +282,8 @@
       nextmkopt = &mkoptions;
       nextappmkopt = &appmkoptions;
       nextfsopt = &fsoptions;
+       moduletab = ht_new();
+       emptyattrtab = ht_new();

       /*
        * Handle profiling (must do this before we try to create any
@@ -363,7 +367,7 @@
       /*
        * Select devices and pseudo devices and their attributes
        */
-       if (fixdevis())
+       if (fixdevis() || fixlkmdevs())
               stop();

       /*
@@ -384,9 +388,20 @@
               stop();

       /*
+        * Fix files for LKMs, then group the files into modules.
+        */
+       if (fixlkmfiles())
+               stop();
+
+       if (Gflag) {
+               if (packlkms())
+                       stop();
+       }
+
+       /*
        * Fix device-majors.
        */
-       if (fixdevsw())
+       if (fixdevsw() || fixlkmdevsw())
               stop();

       /*
@@ -421,7 +436,8 @@
        * Ready to go.  Build all the various files.
        */
       if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() ||
-           mkioconf() || (do_devsw ? mkdevsw() : 0) || mkident())
+           mkioconf() || (do_devsw ? mkdevsw() : 0) || mkident() ||
+           mklkms())
               stop();
       (void)printf("Build directory is %s\n", builddir);
       (void)printf("Don't forget to run \"make depend\"\n");
@@ -481,7 +497,7 @@
                       if (a->a_iattr)
                               panic("do_depend(%s): dep `%s' is an iattr",
                                   nv->nv_name, a->a_name);
-                       expandattr(a, selectattr);
+                       expandattr(a, NULL, selectattr);
               } else {
                       if (ht_lookup(opttab, nv->nv_name) == NULL)
                               addoption(nv->nv_name, NULL);
@@ -878,7 +894,7 @@
 * file system type.  The name is then treated like a standard option.
 */
void
-addfsoption(const char *name)
+addfsoption(const char *name, int module)
{
       const char *n;

@@ -888,6 +904,12 @@
               return;
       }

+       /* XXX */
+       if (module) {
+               cfgerror("module for file-system %s: not supported yet", name);
+               return;
+       }
+
       /*
        * Convert to lower case.  This will be used in the select
        * table, to verify root file systems.
@@ -1206,11 +1228,7 @@
       if (builddir == NULL)
               builddir = defbuilddir;

-       if (stat(builddir, &st) == -1) {
-               if (mkdir(builddir, 0777) == -1)
-                       errx(EXIT_FAILURE, "cannot create %s", builddir);
-       } else if (!S_ISDIR(st.st_mode))
-               errx(EXIT_FAILURE, "%s is not a directory", builddir);
+       make_directory(builddir);
       if (chdir(builddir) == -1)
               err(EXIT_FAILURE, "cannot change to %s", builddir);
       if (stat(srcdir, &st) == -1)
@@ -1519,16 +1537,22 @@
 * may have special considerations regarding ignored instances.
 */

-static int
+int
devbase_has_any_instance(struct devbase *dev, int unit, int state, int level)
{
       struct deva *da;
       struct devi *i;

       if (dev->d_ispseudo) {
-               if (dev->d_ihead != NULL)
-                       return 1;
-               else if (state != DEVI_IGNORED)
+               if (dev->d_ihead != NULL) {
+                       i = dev->d_ihead;
+                       if (state == DEVI_MODULE && i->i_module)
+                               return 1;
+                       if (state == DEVI_ACTIVE && i->i_module == NULL)
+                               return 1;
+               }
+
+               if (state != DEVI_IGNORED)
                       return 0;
               if ((i = ht_lookup(deaddevitab, dev->d_name)) == NULL)
                       return 0;
@@ -1538,6 +1562,7 @@
       for (da = dev->d_ahead; da != NULL; da = da->d_bsame)
               for (i = da->d_ihead; i != NULL; i = i->i_asame)
                       if ((i->i_active == DEVI_ACTIVE ||
+                            i->i_active == DEVI_MODULE ||
                            i->i_active == state) &&
                           (unit == WILD || unit == i->i_unit ||
                            i->i_unit == STAR))
@@ -1613,7 +1638,9 @@
        */
       if (d->d_ispseudo) {
               if (d->d_ihead != NULL)
-                       d->d_ihead->i_active = active = DEVI_ACTIVE;
+                       d->d_ihead->i_active = active =
+                               d->d_ihead->i_module != NULL ?
+                               DEVI_MODULE : DEVI_ACTIVE;
               else {
                       if (ht_lookup(deaddevitab, d->d_name) != NULL)
                               active = DEVI_IGNORED;
@@ -1623,41 +1650,84 @@
       } else {
               int seen = 0;

-               for (i = d->d_ihead; i != NULL; i = i->i_bsame) {
-                       for (j = i; j != NULL; j = j->i_alias) {
-                               p = j->i_pspec;
-                               if ((p == NULL && at == NULL) ||
-                                   (p != NULL && p->p_iattr == at &&
-                                   (p->p_atdev == NULL ||
-                                   p->p_atdev == parent))) {
-                                       if (p != NULL &&
-                                           !devbase_has_any_instance(parent,
-                                             p->p_atunit, state, j->i_level))
-                                               continue;
+#define        FOR_ALL_ALIASES(i, j, d) \
+       for (i = d->d_ihead; i != NULL; i = i->i_bsame) \
+               for (j = i; j != NULL; j = j->i_alias)
+
+               FOR_ALL_ALIASES(i, j, d) {
+                       p = j->i_pspec;
+                       if ((p == NULL && at == NULL) ||
+                           (p != NULL && p->p_iattr == at &&
+                            (p->p_atdev == NULL ||
+                             p->p_atdev == parent))) {
+                               if (p != NULL &&
+                                   !devbase_has_any_instance(parent,
+                                                             p->p_atunit, state, j->i_level))
+                                       continue;
+                               /*
+                                * There are Fry-like devices which can
+                                * be their own grand-parent (or even
+                                * parent, like uhub).  We don't want
+                                * to loop, so if we've already reached
+                                * an instance for one reason or
+                                * another, stop there.
+                                */
+                               if (j->i_active == DEVI_ACTIVE ||
+                                   j->i_active == DEVI_MODULE ||
+                                   j->i_active == state) {
                                       /*
-                                        * There are Fry-like devices which can
-                                        * be their own grand-parent (or even
-                                        * parent, like uhub).  We don't want
-                                        * to loop, so if we've already reached
-                                        * an instance for one reason or
-                                        * another, stop there.
+                                        * Device has already been
+                                        * seen.  However it might
+                                        * have siblings who still
+                                        * have to be activated or
+                                        * orphaned.
                                        */
-                                       if (j->i_active == DEVI_ACTIVE ||
-                                           j->i_active == state) {
-                                               /*
-                                                * Device has already been
-                                                * seen.  However it might
-                                                * have siblings who still
-                                                * have to be activated or
-                                                * orphaned.
-                                                */
-                                               seen = 1;
-                                               continue;
-                                       }
+                                       seen = 1;
+                                       continue;
+                               }
+
+                               if (state == DEVI_MODULE &&
+                                   j->i_module == NULL) {
+
+                                       /* We are looking at children
+                                          of a LKM device.  leave
+                                          this non-LKM device as
+                                          orphan.
+                                       */
+                                       printf("Leave as orphan %s\n",
+                                              j->i_name);
+                                       continue;
+                               }
+
+                               if (j->i_module) {
+                                       j->i_active =
+                                               state == DEVI_ACTIVE ?
+                                               DEVI_MODULE : state;
+                                       if (active != DEVI_ACTIVE)
+                                               active = j->i_active;
+                               }
+                               else {
                                       j->i_active = active = state;
-                                       if (p != NULL)
-                                               p->p_active = state;
                               }
+
+                               if (p == NULL)
+                                       continue;
+
+                               /* change p->p_active:
+                                    ORPHAN->ACTIVE or MODULE
+                                    ACTIVE->MODULE */
+
+                               if (p->p_active == DEVI_ORPHAN)
+                                       p->p_active = active;
+                               else {
+#if 0
+                                       printf("%s: p_active: %d %d\n",
+                                              p->p_iattr->a_name,
+                                              p->p_active, active);
+#endif
+                                       if (active == DEVI_ACTIVE)
+                                               p->p_active = active;
+                               }
                       }
               }
               /*
@@ -1686,10 +1756,19 @@
               }
       }

+       if (active == DEVI_MODULE)
+               active = DEVI_ACTIVE;
+
       for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
               a = nv->nv_ptr;
-               for (nv1 = a->a_devs; nv1 != NULL; nv1 = nv1->nv_next)
+               for (nv1 = a->a_devs; nv1 != NULL; nv1 = nv1->nv_next) {
+#if 0
+                       struct devbase *b  = nv1->nv_ptr;
+                       printf(" attr %s=>dev %s state/active=%d/%d\n",
+                              a->a_name, b->d_name, state, active);
+#endif
                       do_kill_orphans(nv1->nv_ptr, a, d, active);
+               }
       }
}

@@ -1704,5 +1783,31 @@
static void
kill_orphans(void)
{
+
       ht_enumerate(devroottab, kill_orphans_cb, NULL);
}
+
+
+#if 0
+/*----------------------------------------------------------------*/
+static void
+find_orphan_modules(void)
+{
+       struct devi *i;
+
+       TAILQ_FOREACH(i, &alldevi, i_next){
+               if (i->i_active == DEVI_ORPHAN &&
+                   i->i_module != NULL) {
+                       printf("%s is orphan module. pspec=%p\n",
+                              i->i_name, i->i_pspec);
+                       if (i->i_pspec) {
+                               printf("   p_iattr=%p p_atdev=%p\n",
+                                      i->i_pspec->p_iattr,
+                                      i->i_pspec->p_atdev);
+                       }
+               }
+       }
+
+}
+#endif
+
Index: sem.c
===================================================================
--- sem.c       (.../vendor/netbsd/current-20080118)    (revision 51)
+++ sem.c       (.../tags/patch-1-rc1)  (revision 51)
@@ -265,7 +265,7 @@
       a->a_expanding = 0;

       /* Expand the attribute to check for cycles in the graph. */
-       expandattr(a, NULL);
+       expandattr(a, NULL, NULL);

       return (0);
}
@@ -592,7 +592,7 @@
 * cycles, and invoking a callback for each attribute found.
 */
void
-expandattr(struct attr *a, void (*callback)(struct attr *))
+expandattr(struct attr *a, void *data, void (*callback)(struct attr *, void *))
{
       struct nvlist *nv;
       struct attr *dep;
@@ -607,12 +607,12 @@
       /* First expand all of this attribute's dependencies. */
       for (nv = a->a_deps; nv != NULL; nv = nv->nv_next) {
               dep = nv->nv_ptr;
-               expandattr(dep, callback);
+               expandattr(dep, data, callback);
       }

       /* ...and now invoke the callback for ourself. */
       if (callback != NULL)
-               (*callback)(a);
+               (*callback)(a, data);

       a->a_expanding = 0;
}
@@ -907,6 +907,7 @@
       i->i_atdeva = NULL;
       i->i_locs = NULL;
       i->i_cfflags = 0;
+       i->i_module = NULL;
       i->i_lineno = currentline();
       i->i_srcfile = yyfile;
       i->i_active = DEVI_ORPHAN; /* Proper analysis comes later */
@@ -921,7 +922,8 @@
 * another device instead) plus unit number.
 */
void
-adddev(const char *name, const char *at, struct nvlist *loclist, int flags)
+adddev(const char *name, const char *at, struct nvlist *loclist, int flags,
+       int module_flag)
{
       struct devi *i;         /* the new instance */
       struct pspec *p;        /* and its pspec */
@@ -1055,6 +1057,7 @@
       i->i_pspec = p;
       i->i_atdeva = iba;
       i->i_cfflags = flags;
+       i->i_module = module_flag ? module_mark : NULL;

       *iba->d_ipp = i;
       iba->d_ipp = &i->i_asame;
@@ -1396,12 +1399,21 @@
}

void
-addpseudo(const char *name, int number)
+addpseudo(const char *name, int number, int module_flag)
{
       struct devbase *d;
       struct devi *i;

       d = ht_lookup(devbasetab, name);
+
+#if 1
+       if (module_flag) { /* XXX */
+               cfgerror("module for pseudo-device %s: not supported yet",
+                        name);
+               return;
+       }
+#endif
+
       if (d == NULL) {
               cfgerror("undefined pseudo-device %s", name);
               return;
@@ -1419,7 +1431,15 @@
               panic("addpseudo(%s)", name);
       /* Useful to retrieve the instance from the devbase */
       d->d_ihead = i;
-       i->i_active = DEVI_ACTIVE;
+
+       if (module_flag) {
+               i->i_active = DEVI_MODULE;
+               i->i_module = module_mark;
+       }
+       else {
+               i->i_active = DEVI_ACTIVE;
+               i->i_module = NULL;
+       }
       TAILQ_INSERT_TAIL(&allpseudo, i, i_next);
}

@@ -1655,7 +1675,7 @@
}

void
-selectattr(struct attr *a)
+selectattr(struct attr *a, void *context)
{

       (void)ht_insert(selecttab, a->a_name, __UNCONST(a->a_name));
@@ -1674,13 +1694,13 @@
       (void)ht_insert(selecttab, d->d_name, __UNCONST(d->d_name));
       for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
               a = nv->nv_ptr;
-               expandattr(a, selectattr);
+               expandattr(a, NULL, selectattr);
       }
       if (da != NULL) {
               (void)ht_insert(selecttab, da->d_name, __UNCONST(da->d_name));
               for (nv = da->d_attrs; nv != NULL; nv = nv->nv_next) {
                       a = nv->nv_ptr;
-                       expandattr(a, selectattr);
+                       expandattr(a, NULL, selectattr);
               }
       }
}
Index: modload.py
===================================================================
--- modload.py  (.../vendor/netbsd/current-20080118)    (revision 0)
+++ modload.py  (.../tags/patch-1-rc1)  (revision 51)
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+"""Wrapper for /sbin/modload
+
+Usage: modload.py [-Mpath1:path2...] [-Adinvs] [-o output] files ...
+
+  -A           automatically load required LKMs.
+  -d           passed to /sbin/modload.
+  -i           print dependencies for LKM files.
+  -M path      module search path.
+  -n           don't run. print what to do.
+  -n           don't run. print what to do.
+  -o output    passed to /sbin/modload.
+  -s           passed to /sbin/modload.
+  -v           be verbose.
+"""
+
+import sys, getopt, os, re
+from sys import stderr
+from os.path import basename, dirname
+
+def usage_and_exit(r):
+  "print usage to stderr, then exit"
+  print >> stderr, """\
+Usage: %(program)s [-Ainvs] [-M path1:path2...] [-o output] files...""" % \
+  {"program": sys.argv[0]}
+  sys.exit(r)
+
+
+#modload_prog = "/bin/echo"
+modload_prog = "/sbin/modload"
+
+options = {}
+lkm_pool = None
+module_path = ['.', "/usr/lkm"]
+
+class LkmPool:
+  def __init__(self):
+    self.all_lkms = {}
+    self.features = {}
+
+  def add(self, lkm):
+    if self.all_lkms.has_key(lkm.basename):
+      # print >> stderr, "LKM file " + lkm.basename + " is ignored."
+      pass
+    else:
+      self.all_lkms[lkm.basename] = lkm
+      for f in lkm.provide:
+        self.features[f] = lkm
+
+  def find(self, req):
+    "Find an LKM that provides required feature"
+    try:
+      return self.features[req]
+    except KeyError:
+      return None
+
+  def scan_dir(self, dir):
+    print >> stderr, "Scanning", dir
+    files = os.listdir(dir)
+    for f in files:
+      ff = os.path.join(dir,f)
+      if os.path.splitext(f)[1] == ".o" and \
+         not os.path.isdir(ff):
+        self.add(LoadableKernelModule(ff))
+
+  def get(self, fname):
+    base = basename(fname)
+    if self.all_lkms.has_key(base):
+      return self.all_lkms[base]
+    new_lkm = LoadableKernelModule(fname)
+    self.all_lkms[base] = new_lkm
+    return new_lkm
+
+class LoadableKernelModule:
+  "A class to represent LKM"
+
+  def read_dependencies(self, offset, size):
+    f = open(self.filename,"r")
+    f.seek(offset)
+    data = f.read(size)
+    f.close()
+    data = data.rstrip('\x00')
+    if len(data) == 0:   # We need this because
+      return []          #   "".split('\x00') returns ['']
+    return data.split('\x00')
+
+  def __init__(self,fname):
+    self.filename = fname
+    self.basename = basename(fname)
+    self.dirname = dirname(fname)
+    self.follows = {}
+    self.provide = self.require = {}
+
+    objdump = os.popen("objdump -h %(fname)s" % {"fname": fname},
+                       "r");
+    for line in objdump:
+      m = re.search(r"^\s*\d+\s+.note.netbsd.lkm.(require|provide)\s*",
+                    line)
+      if m:
+        reqpro = m.group(1)
+        postmatch = line[m.end():]
+        size, vma, lma, fileoff, align = postmatch.split()
+        a = self.read_dependencies(int(fileoff,16),
+                                   int(size, 16))
+        if reqpro == "require":
+          self.require = a
+        else:
+          self.provide = a
+
+    objdump.close()
+
+
+  def __str__(self):
+    return "LKM<" + self.filename + ">"
+
+
+  def print_info(self):
+    print self.filename, "requires:"
+    for a in self.require:
+      print "\t", a
+    print self.filename, "provides:"
+    for a in self.provide:
+      print "\t", a
+
+#
+# for modload -i files...
+#
+def list_provide_and_require(fname):
+  "print LKM depedency informations for `filename'"
+  lkm = lkm_pool.get(fname)
+  lkm.print_info()
+
+def resolve(files):
+  """find all LKMs required by ones in `files',
+  and sort all LKMs in loading order"""
+
+  dir_searched = False
+  lkms_to_load = {}
+
+  for f in files:
+    lkm = LoadableKernelModule(f)
+    lkms_to_load[lkm] = True
+    lkm_pool.add(lkm)
+
+  def scan_dirs(dirs):
+    for dir in dirs:
+      if dir == "":
+        dir = "."
+      lkm_pool.scan_dir(dir)
+
+  changed = True
+  while changed:
+    changed = False
+    for o in lkms_to_load.keys():
+      # print >> stderr, "Checking for", o
+      for d in o.require:
+        # print >> stderr, "  requires", d
+        lkm = lkm_pool.find(d)
+        if lkm:
+          # print >> stderr, "   provided by", lkm
+          if lkms_to_load.has_key(lkm):
+            pass # already in the list
+          else:
+            lkms_to_load[lkm] = o
+            changed = True
+            o.follows[lkm] = True
+        else:
+          if not dir_searched:
+            scan_dirs( module_path )
+            dir_searched = True
+            changed = True
+          else:
+            print >> stderr, "No modules for feature", d
+
+# for k, v in lkms_to_load.iteritems():
+#   print k, "<-", v
+#   print k.require
+
+  order = []
+  while len(lkms_to_load) > 0:
+    a = lkms_to_load.keys()
+    for lkm in a:
+      leaf = True
+      for p in lkm.follows.keys():
+        if lkms_to_load.has_key(p):
+          leaf = False
+      if leaf:
+        order.append(lkm)
+        del lkms_to_load[lkm]
+  return order
+
+def run_modload(file):
+  args = [modload_prog]
+  for o in ["-o", "-v", "-d"]:
+    if options.has_key(o): args.append(o + options[o])
+  if options.has_key("-s") or options.has_key("-A"): args.append("-s")
+  args.append(file)
+
+  if options.has_key("-n"):
+    if options.has_key("-v"): print "Loading", file, "..."
+    print " ".join(args)
+    status = 0
+  else:
+    if options.has_key("-v"): print >> stderr, "Loading", file + "..."
+    status = os.spawnv(os.P_WAIT, modload_prog, args)
+  return status
+
+
+def main():
+  global lkm_pool
+  global options
+  global module_path
+
+  try:
+    opts, args = getopt.getopt(sys.argv[1:], "AdiM:nvso:")
+  except getopt.GetoptError, errmsg:
+    print >> stderr, "%(program)s: %(errmsg)s" %\
+          {"program": sys.argv[0], "errmsg": errmsg}
+    usage_and_exit(2)
+
+  lkm_pool = LkmPool()
+
+  for o, a in opts:
+    options[o] = a
+
+  if len(args) == 0:
+    print >> stderr, "Too few arguments"
+    usage_and_exit(2)
+
+  if options.has_key("-i"):
+    for f in args:
+      list_provide_and_require(f)
+    sys.exit(0)
+
+  if options.has_key("-M"):
+    module_path = options["-M"].split(":")
+  else:
+    module_path = [ dirname(args[0]), "/usr/lkm" ]
+
+  if not options.has_key("-A"):
+    err = 0
+    for f in args:
+      if run_modload(f) != 0: err += 1
+    sys.exit(err)
+
+  load_order = resolve(args)
+
+  for lkm in load_order:
+    run_modload(lkm.filename)
+
+if __name__ == "__main__":
+  main()

Property changes on: modload.py
___________________________________________________________________
Name: svn:executable
  + *

Index: defs.h
===================================================================
--- defs.h      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ defs.h      (.../tags/patch-1-rc1)  (revision 51)
@@ -208,6 +208,8 @@
       struct  deva *d_ahead;          /* first attachment, if any */
       struct  deva **d_app;           /* used for tacking on attachments */
       struct  attr *d_classattr;      /* device class attribute (if any) */
+       struct  module *d_module;     /* this device is compiled in the LKM */
+       STAILQ_ENTRY(devbase) d_mnext;  /* devices in the same LKM */
};

struct deva {
@@ -220,6 +222,9 @@
       struct  nvlist *d_attrs;        /* attributes, if any */
       struct  devi *d_ihead;          /* first instance, if any */
       struct  devi **d_ipp;           /* used for tacking on more instances */
+       struct  module *d_module; /* this device is compiled in the LKM */
+       STAILQ_ENTRY(deva) d_mnext;  /* devices in the same LKM */
+
};

/*
@@ -241,6 +246,8 @@
       struct  devi *i_bsame;  /* list on same base */
       struct  devi *i_asame;  /* list on same base attachment */
       struct  devi *i_alias;  /* other aliases of this instance */
+       struct  module *i_module; /* this device is compiled in the LKM */
+       STAILQ_ENTRY(devi) i_mnext;  /* devices in the same LKM */
       const char *i_at;       /* where this is "at" (NULL if at root) */
       struct  pspec *i_pspec; /* parent spec (NULL if at root) */
       struct  deva *i_atdeva;
@@ -254,6 +261,7 @@
#define        DEVI_ACTIVE     1       /* instance has an active parent */
#define        DEVI_IGNORED    2       /* instance's parent has been removed */
#define DEVI_BROKEN    3       /* instance is broken (syntax error) */
+#define        DEVI_MODULE     4       /* instance is compiled as LKM */

       /* created during packing or ioconf.c generation */
       short   i_collapsed;    /* set => this alias no longer needed */
@@ -277,6 +285,10 @@
       char    fit_lastc;      /* last char from path */
       const char *fit_path;   /* full file path */
       const char *fit_prefix; /* any file prefix */
+
+       STAILQ_ENTRY(filetype) fit_msame;  /* list of files in the same module */
+       struct nvlist *fit_modules; /* list of modules which need this file */
+       int fit_nmods;  /* the number of modules which need this file */
};
/* Anything less than 0x10 is sub-type specific */
#define FIT_NOPROLOGUE  0x10    /* Don't prepend $S/ */
@@ -310,12 +322,14 @@
#define fi_lastc   fi_fit.fit_lastc
#define fi_path    fi_fit.fit_path
#define fi_prefix  fi_fit.fit_prefix
+#define fi_msame   fi_fit.fit_msame

/* flags */
#define        FI_SEL          0x01    /* selected */
#define        FI_NEEDSCOUNT   0x02    /* needs-count */
#define        FI_NEEDSFLAG    0x04    /* needs-flag */
#define        FI_HIDDEN       0x08    /* obscured by other(s), base names overlap */
+#define        FI_MODULE       0x10    /* Used only for modules */

/*
 * Objects and libraries.  This allows precompiled object and library
@@ -324,8 +338,8 @@
struct objects {
       struct  filetype oi_fit;
       TAILQ_ENTRY(objects) oi_next;
-       struct  nvlist *oi_optx;/* options expression */
-       struct  nvlist *oi_optf;/* flattened version of above, if needed */
+       struct  nvlist *oi_optx;    /* options expression */
+       struct  nvlist *oi_optf;    /* flattened version of above, if needed */
};

#define oi_srcfile oi_fit.fit_srcfile
@@ -334,6 +348,7 @@
#define oi_lastc   oi_fit.fit_lastc
#define oi_path    oi_fit.fit_path
#define oi_prefix  oi_fit.fit_prefix
+#define oi_msame   oi_fit.fit_msame

/* flags */
#define        OI_SEL          0x01    /* selected */
@@ -364,9 +379,64 @@
       int             dm_cmajor;      /* character major */
       int             dm_bmajor;      /* block major */
       struct nvlist   *dm_opts;       /* options */
+       enum { DEVM_IDLE, DEVM_MODULE, DEVM_INKERNEL }  dm_active;
};

/*
+ * Modules.
+ */
+struct module {
+       const char      *m_name;
+       struct  hashtab *m_selecttab;   /* selects things that goes to
+                                        * this module  */
+       struct hashtab *m_require; /* modules this module needs */
+
+       enum module_type {
+               M_DEVICE,
+               M_PSEUDO,
+               M_FILESYSTEM,
+               M_ATTRIBUTE,
+#if 1
+               M_MERGE
+#else
+               M_FILEGROUP
+#endif
+       } m_type;
+
+       union {
+               /* for M_DEVICE */
+               struct {
+                       STAILQ_HEAD(, devbase) md_base;
+                       STAILQ_HEAD(, deva) md_deva;
+                       STAILQ_HEAD(, devi) md_devi;
+               } m_dev;
+
+               /* M_ATTRIBUTE */
+               struct attr *m_attr;
+       } u;
+
+       struct files *m_lkmfile; /* _MODULE_lkm.c */
+       STAILQ_HEAD(, filetype) m_files;
+
+       struct nvlist *m_modules;       /* M_MERGE */
+       struct module *m_merged; /* merged into the other module */
+};
+
+#define        m_base  u.m_dev.md_base
+#define        m_deva  u.m_dev.md_deva
+#define        m_devi  u.m_dev.md_devi
+
+/* No files for the module. */
+#define        m_empty(m)      ((m)->m_lkmfile == NULL && STAILQ_EMPTY(&(m)->m_files))
+
+/*
+ * Used to indicate a device is compiled as an LKM.
+ * struct devi.i_module is first set to this value, then
+ * changed to a correct struct module object.
+ */
+#define        module_mark     ((struct module *)0x00000001)
+
+/*
 * Hash tables look up name=value pairs.  The pointer value of the name
 * is assumed to be constant forever; this can be arranged by interning
 * the name.  (This is fairly convenient since our lexer does this for
@@ -415,6 +485,8 @@
struct hashtab *attrtab;       /* attributes (locators, etc.) */
struct hashtab *bdevmtab;      /* block devm lookup */
struct hashtab *cdevmtab;      /* character devm lookup */
+struct hashtab *moduletab;     /* LKM lookup */
+struct hashtab *emptyattrtab;  /* attributes with no codes */

TAILQ_HEAD(, devbase)  allbases;       /* list of all devbase structures */
TAILQ_HEAD(, deva)     alldevas;       /* list of all devbase attachments */
@@ -458,8 +530,10 @@
int    fixfiles(void);         /* finalize */
int    fixobjects(void);
int    fixdevsw(void);
-void   addfile(const char *, struct nvlist *, int, const char *);
+struct files   *addfile(const char *, struct nvlist *, int, const char *);
void   addobject(const char *, struct nvlist *, int);
+int    expr_eval(struct nvlist *, int (*)(const char *, void *), void *);
+char   *expr_canonstr(struct nvlist *);

/* hash.c */
struct hashtab *ht_new(void);
@@ -473,6 +547,8 @@
const char *intern(const char *);
typedef int (*ht_callback)(const char *, void *, void *);
int    ht_enumerate(struct hashtab *, ht_callback, void *);
+int    ht_isempty(struct hashtab *);
+int    ht_concat(struct hashtab *, struct hashtab *, int);

/* lint.c */
void   emit_instances(void);
@@ -481,7 +557,7 @@

/* main.c */
void   addoption(const char *, const char *);
-void   addfsoption(const char *);
+void   addfsoption(const char *, int);
void   addmkoption(const char *, const char *);
void   appendmkoption(const char *, const char *);
void   appendcondmkoption(const char *, const char *, const char *);
@@ -493,6 +569,8 @@
void   delfsoption(const char *);
void   delmkoption(const char *);
int    devbase_has_instances(struct devbase *, int);
+int    devbase_has_any_instance(struct devbase *, int, int, int);
+
struct nvlist * find_declared_option(const char *);
int    deva_has_instances(struct deva *, int);
void   setupdirs(void);
@@ -517,9 +595,22 @@

/* mkioconf.c */
int    mkioconf(void);
+void ioconf_emithdr(FILE *, const char *);
+void ioconf_cfdata_start(FILE *, const char *);
+void ioconf_cfdata_entry(FILE *, struct devi *);
+void ioconf_cfdata_end(FILE *, const char *);
+void ioconf_emit_pspec(FILE *, struct pspec *, int);
+void ioconf_emit_cfdriver(struct devbase *, FILE *);
+void ioconf_emit_ref_iattrs(struct devbase *d, FILE *fp);

/* mkmakefile.c */
int    mkmakefile(void);
+struct makefile_constructs {
+       const char *str;
+       void (* func)(FILE *, void *);
+};
+int    mkmakefile_common(const struct makefile_constructs *,
+                         const char *, const char *, void *);

/* mkswap.c */
int    mkswap(void);
@@ -527,6 +618,15 @@
/* pack.c */
void   pack(void);

+/* lkm.c */
+int    fixlkmdevs(void);
+int    fixlkmfiles(void);
+int    packlkms(void);
+int    mklkms(void);
+void   emitkmod(FILE *, void *);
+void   dump_module_devis(void);
+int    fixlkmdevsw(void);
+
/* scan.l */
int    currentline(void);
int    firstfile(const char *);
@@ -556,6 +656,7 @@
void   nvfreel(struct nvlist *);
struct nvlist *nvcat(struct nvlist *, struct nvlist *);
void   autogen_comment(FILE *, const char *);
+void   make_directory(const char *);

/* liby */
void   yyerror(const char *);
Index: moddep
===================================================================
--- moddep      (.../vendor/netbsd/current-20080118)    (revision 0)
+++ moddep      (.../tags/patch-1-rc1)  (revision 51)
@@ -0,0 +1,32 @@
+#!/usr/pkg/bin/perl
+
+use English;
+
+for $f (@ARGV) {
+    die "can't find $f" unless -r $f;
+
+    open(SECTION, "objdump -h $f|") || die "$!";
+    while (<SECTION>) {
+
+       if (/^\s*\d+\s+.note.netbsd.lkm.(require|provide)\s*/) {
+           $reqpro = $1;
+           ($size, $vma, $lma, $fileoff, $align) = split(/\s+/, $POSTMATCH);
+           $D{$reqpro}->{OFF} = hex $fileoff;
+           $D{$reqpro}->{SIZE} = hex $size;
+       }
+    }
+    close(SECTION);
+
+    open(LKM, $f) || die "$!";
+    for $k (keys %D) {
+
+       seek LKM, $D{$k}->{OFF}, 0;
+       read LKM, $_, $D{$k}->{SIZE};
+       $D{$k}->{DATA} = $_;
+
+       print "$f ${k}s:\n";
+       map {print "\t$_\n";} split(/\000/, $_);
+    }
+
+
+}

Property changes on: moddep
___________________________________________________________________
Name: svn:executable
  + *

Index: sem.h
===================================================================
--- sem.h       (.../vendor/netbsd/current-20080118)    (revision 51)
+++ sem.h       (.../tags/patch-1-rc1)  (revision 51)
@@ -53,18 +53,18 @@
struct devbase *getdevbase(const char *);
struct deva    *getdevattach(const char *);
struct attr    *getattr(const char *);
-void           expandattr(struct attr *, void (*)(struct attr *));
-void           selectattr(struct attr *);
+void           expandattr(struct attr *, void *, void (*)(struct attr *, void *));
+void           selectattr(struct attr *, void *);
void           setmajor(struct devbase *, int);
void           addconf(struct config *);
void           setconf(struct nvlist **, const char *, struct nvlist *);
void           delconf(const char *);
void           setfstype(const char **, const char *);
-void           adddev(const char *, const char *, struct nvlist *, int);
+void           adddev(const char *, const char *, struct nvlist *, int, int);
void           deldevi(const char *, const char *);
void           deldeva(const char *);
void           deldev(const char *);
-void           addpseudo(const char *, int);
+void           addpseudo(const char *, int, int);
void           delpseudo(const char *);
void           adddevm(const char *, int, int, struct nvlist *);
int            fixdevis(void);
Index: Makefile
===================================================================
--- Makefile    (.../vendor/netbsd/current-20080118)    (revision 51)
+++ Makefile    (.../tags/patch-1-rc1)  (revision 51)
@@ -5,7 +5,7 @@

PROG=  config
SRCS=  files.c gram.y hash.c lint.c main.c mkdevsw.c mkheaders.c mkioconf.c \
-       mkmakefile.c mkswap.c pack.c scan.l sem.c util.c
+       mkmakefile.c mkswap.c pack.c lkm.c scan.l sem.c util.c

.PATH: ${NETBSDSRCDIR}/usr.bin/cksum
SRCS+=  crc.c
Index: scan.l
===================================================================
--- scan.l      (.../vendor/netbsd/current-20080118)    (revision 51)
+++ scan.l      (.../tags/patch-1-rc1)  (revision 51)
@@ -149,6 +149,7 @@
maxpartitions  return MAXPARTITIONS;
maxusers       return MAXUSERS;
minor          return MINOR;
+module         return MODULE;
needs-count    return NEEDS_COUNT;
needs-flag     return NEEDS_FLAG;
no             return NO;

Property changes on: .
___________________________________________________________________
Name: svn:ignore
  + TAGS
obj.*



Index: files.c
===================================================================
--- files.c     (.../vendor/netbsd/current-20080118)    (revision 51)
+++ files.c     (.../tags/patch-1-rc1)  (revision 51)
@@ -69,9 +69,10 @@
static int     fixcount(const char *, void *);
static int     fixfsel(const char *, void *);
static int     fixsel(const char *, void *);
-static int     expr_eval(struct nvlist *,
-                   int (*)(const char *, void *), void *);
static void    expr_free(struct nvlist *);
+//static char  *expr_canonstr_sub(struct nvlist *, char *, size_t);
+static int     getlastc(const char *);
+static int     fixdevmtab_sub(int, struct hashtab *, struct devm *);

void
initfiles(void)
@@ -84,7 +85,7 @@
       TAILQ_INIT(&allobjects);
}

-void
+struct files *
addfile(const char *path, struct nvlist *optx, int flags, const char *rule)
{
       struct files *fi;
@@ -140,7 +141,7 @@
               if (rule != NULL && optx == NULL && flags == 0 &&
                   yyfile != fi->fi_srcfile) {
                       fi->fi_mkrule = rule;
-                       return;
+                       return NULL;
               }
               cfgerror("duplicate file %s", path);
               cfgxerror(fi->fi_srcfile, fi->fi_srcline,
@@ -161,9 +162,10 @@
       fi->fi_optf = NULL;
       fi->fi_mkrule = rule;
       TAILQ_INSERT_TAIL(&allfiles, fi, fi_next);
-       return;
+       return fi;
 bad:
       expr_free(optx);
+       return NULL;
}

void
@@ -188,6 +190,7 @@
       oi->oi_srcline = currentline();
       oi->oi_flags = flags;
       oi->oi_path = path;
+       oi->oi_lastc = getlastc(path);
       oi->oi_prefix = SLIST_EMPTY(&prefixes) ? NULL :
                       SLIST_FIRST(&prefixes)->pf_prefix;
       oi->oi_optx = optx;
@@ -251,8 +254,9 @@
       err = 0;
       TAILQ_FOREACH(fi, &allfiles, fi_next) {

-               /* Skip files that generated counted-device complaints. */
-               if (fi->fi_flags & FI_HIDDEN)
+               /* Skip files that generated counted-device complaints.
+                  or that are only for modules (ex. _MOD_lkm.c) */
+               if (fi->fi_flags & (FI_HIDDEN|FI_MODULE))
                       continue;

               /* Optional: see if it is to be included. */
@@ -343,7 +347,7 @@
       int error;
       struct devm *dm, *res;
       struct hashtab *fixdevmtab;
-       char mstr[16];
+       struct devbase *d;

       error = 0;
       fixdevmtab = ht_new();
@@ -378,53 +382,39 @@
                             dm->dm_name, dm->dm_cmajor, dm->dm_bmajor);
               }

-               if (dm->dm_opts != NULL &&
-                   !expr_eval(dm->dm_opts, fixsel, NULL))
+               if (dm->dm_opts == NULL ||
+                   expr_eval(dm->dm_opts, fixsel, NULL)) {
+#if 0
+                       printf("devm %s  bmajor=%d cmajor=%d\n",
+                              dm->dm_name, dm->dm_bmajor, dm->dm_cmajor);
+#endif
+                       dm->dm_active = DEVM_INKERNEL;
+               }
+               else if ((d = ht_lookup(devbasetab, dm->dm_name)) != NULL &&
+                        d->d_module != NULL) {
+#if 0
+                       printf("devm %s base=%s bmajor=%d cmajor=%d\n",
+                              dm->dm_name, d->d_name,
+                              dm->dm_bmajor, dm->dm_cmajor);
+#endif
+                       dm->dm_active = DEVM_MODULE;
+               }
+               else {
                       continue;
+               }

+
               if (dm->dm_cmajor != -1) {
-                       if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) {
-                               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
-                                      "device-major of character device '%s' "
-                                      "is already defined", dm->dm_name);
+                       if (fixdevmtab_sub(0, cdevmtab, dm)) {
                               error = 1;
                               goto out;
                       }
-                       (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor);
-                       if (ht_lookup(cdevmtab, intern(mstr)) != NULL) {
-                               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
-                                      "device-major of character major '%d' "
-                                      "is already defined", dm->dm_cmajor);
-                               error = 1;
-                               goto out;
-                       }
-                       if (ht_insert(cdevmtab, intern(dm->dm_name), dm) ||
-                           ht_insert(cdevmtab, intern(mstr), dm)) {
-                               panic("fixdevsw: %s character major %d",
-                                     dm->dm_name, dm->dm_cmajor);
-                       }
               }
               if (dm->dm_bmajor != -1) {
-                       if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) {
-                               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
-                                      "device-major of block device '%s' "
-                                      "is already defined", dm->dm_name);
+                       if (fixdevmtab_sub(1, bdevmtab, dm)) {
                               error = 1;
                               goto out;
                       }
-                       (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor);
-                       if (ht_lookup(bdevmtab, intern(mstr)) != NULL) {
-                               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
-                                      "device-major of block major '%d' "
-                                      "is already defined", dm->dm_bmajor);
-                               error = 1;
-                               goto out;
-                       }
-                       if (ht_insert(bdevmtab, intern(dm->dm_name), dm) ||
-                           ht_insert(bdevmtab, intern(mstr), dm)) {
-                               panic("fixdevsw: %s block major %d",
-                                     dm->dm_name, dm->dm_bmajor);
-                       }
               }
       }

@@ -433,6 +423,57 @@
       return (error);
}

+static int
+fixdevmtab_sub(int block, struct hashtab *ht, struct devm *dm)
+{
+       char mstrbuf[16];
+       const char *mstr;
+       const char *type = block ? "block" : "character";
+       int maj = block ? dm->dm_bmajor : dm->dm_cmajor;
+       struct devm *dm2;
+       const char *name = intern(dm->dm_name);
+
+       dm2 = ht_lookup(ht, name);
+       if (dm2 == NULL) {
+               if (ht_insert(ht, name, dm))
+                       goto bad;
+       }
+       else if (dm->dm_active == DEVM_INKERNEL ||
+                dm2->dm_active == DEVM_INKERNEL) {
+               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
+                         "device-major of %s device '%s' is already defined",
+                         type, dm->dm_name);
+               return -1;
+       }
+
+       (void)snprintf(mstrbuf, sizeof(mstrbuf), "%d", maj);
+       mstr = intern(mstrbuf);
+       dm2 = ht_lookup(ht, mstr);
+       if (dm2 == NULL) {
+               if (ht_insert(ht, mstr, dm))
+                       goto bad;
+       }
+       else if (dm2->dm_active == DEVM_INKERNEL ||
+                dm->dm_active == DEVM_INKERNEL) {
+               cfgxerror(dm->dm_srcfile, dm->dm_srcline,
+                         "device-major of %s major '%d' is already defined"
+                         ": %s, %s",
+                         type, maj, dm2->dm_name, dm->dm_name);
+               return -1;
+       }
+
+#if 0
+       printf("%s: %s: bmajor=%d cmajor=%d\n",
+              __FUNCTION__,
+              dm->dm_name, dm->dm_bmajor, dm->dm_cmajor);
+#endif
+       return 0;
+ bad:
+       panic("fixdevsw: ht_insert FAILED: %s %s major %d", dm->dm_name, type, maj);
+       /*NOTREACHED*/
+       return -1;              /* this is bogus */
+}
+
/*
 * Called when evaluating a needs-count expression.  Make sure the
 * atom is a countable device.  The expression succeeds iff there
@@ -496,7 +537,7 @@
 * No short circuiting ever occurs.  fn must return 0 or 1 (otherwise
 * our mixing of C's bitwise & boolean here may give surprises).
 */
-static int
+int
expr_eval(struct nvlist *expr, int (*fn)(const char *, void *), void *context)
{
       int lhs, rhs;
@@ -555,15 +596,95 @@
       }
}

+#if 0
+#define        EXPR_STR_LEN    256
+/*
+ * Generate string form of expr.
+ * Returns the same string for logically equiverent exprs.  ex:
+ *   both a | b & c  and c & b | a  make "a|(b&c)".
+ * XXX:
+ *  but it doesn't canonicalize !(a&b) and !a|!b to the same string.
+ */
+char *
+expr_canonstr(struct nvlist *expr)
+{
+       char buf[EXPR_STR_LEN];
+
+       return estrdup(expr_canonstr_sub(expr, buf, sizeof buf));
+}
+
+
+static char *
+expr_canonstr_sub(struct nvlist *expr, char *buf, size_t buflen)
+{
+       char buf_r[EXPR_STR_LEN], buf_l[EXPR_STR_LEN];
+       int op;
+       const char *lo, *lc, *ro, *rc;
+
+       switch (expr->nv_int) {
+       case FX_ATOM:
+               snprintf(buf, buflen, "%s", expr->nv_name);
+               break;
+       case FX_NOT:
+               snprintf(buf, buflen, "!%s", expr_canonstr_sub(expr->nv_next,
+                                                              buf_r,
+                                                              sizeof buf_r));
+               break;
+       case FX_AND:
+       case FX_OR:
+               lo = ro = "";   /* open */
+               lc = rc = "";   /* close */
+
+               expr_canonstr_sub(expr->nv_ptr, buf_l, sizeof buf_l);
+               if (expr->nv_int < ((struct nvlist *)(expr->nv_ptr))->nv_int) {
+                       lo = "("; lc = ")";
+               }
+
+               expr_canonstr_sub(expr->nv_next, buf_r, sizeof buf_r);
+               if (expr->nv_int < expr->nv_next->nv_int) {
+                       ro = "("; rc = ")";
+               }
+
+               op = expr->nv_int == FX_AND ? '&' : '|';
+
+               if (strcmp(buf_l, buf_r) <= 0)
+                       snprintf(buf, buflen, "%s%s%s%c%s%s%s",
+                                lo, buf_l, lc,
+                                op,
+                                ro, buf_r, rc);
+               else
+                       snprintf(buf, buflen, "%s%s%s%c%s%s%s",
+                                ro, buf_r, rc,
+                                op,
+                                lo, buf_l, lc);
+               break;
+       default:
+               panic("expr_canonstr %d", expr->nv_int);
+       }
+
+       return buf;
+}
+#endif
+
+static int
+getlastc(const char *path)
+{
+       int len;
+       len = strlen(path);
+       if (len <= 0)
+               return '\0';
+       return path[len-1];
+}
+
#ifdef DEBUG
+static void pr0();
+
/*
 * Print expression tree.
 */
void
prexpr(struct nvlist *expr)
{
-       static void pr0();
-
       printf("expr =");
       pr0(expr);
       printf("\n");