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");
