Module Name:    src
Committed By:   thorpej
Date:           Tue Aug  3 15:00:15 UTC 2021

Modified Files:
        src/sys/kern [thorpej-cfargs2]: subr_autoconf.c
        src/sys/sys [thorpej-cfargs2]: device.h

Log Message:
Address concerns about limited compile-time type checking with the
tag-value mechanism of specifying arguments to config_search(),
config_found(), and config_attach() by replacing the tag-value scheme
with a "struct cfargs", a pointer to which is passed to the aforementioned
functions instead.

The structure has a version field to allow for future ABI versioning
flexibility.  The external structure is canononicalized internally
before use.

To ease the initialization of this structure, use a variadic preprocessor
macro, CFARGS(), to construct an anonymous "struct cfargs" inline, the
address of which is passed to the target function.  A CFARGS_NONE macro
provides a symbolic stand-in for when the caller doesn't want to pass
arguments (currently expands to NULL and is handled during canonicalization).


To generate a diff of this commit:
cvs rdiff -u -r1.288 -r1.288.2.1 src/sys/kern/subr_autoconf.c
cvs rdiff -u -r1.171 -r1.171.2.1 src/sys/sys/device.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/kern/subr_autoconf.c
diff -u src/sys/kern/subr_autoconf.c:1.288 src/sys/kern/subr_autoconf.c:1.288.2.1
--- src/sys/kern/subr_autoconf.c:1.288	Mon Jun 14 08:55:49 2021
+++ src/sys/kern/subr_autoconf.c	Tue Aug  3 15:00:15 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: subr_autoconf.c,v 1.288 2021/06/14 08:55:49 skrll Exp $ */
+/* $NetBSD: subr_autoconf.c,v 1.288.2.1 2021/08/03 15:00:15 thorpej Exp $ */
 
 /*
  * Copyright (c) 1996, 2000 Christopher G. Demetriou
@@ -76,10 +76,8 @@
  *	@(#)subr_autoconf.c	8.3 (Berkeley) 5/17/94
  */
 
-#define	__SUBR_AUTOCONF_PRIVATE	/* see <sys/device.h> */
-
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.288 2021/06/14 08:55:49 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.288.2.1 2021/08/03 15:00:15 thorpej Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -168,6 +166,20 @@ struct alldevs_foray {
 	struct devicelist	af_garbage;
 };
 
+/*
+ * Internal version of the cfargs structure; all versions are
+ * canonicalized to this.
+ */
+struct cfargs_internal {
+	union {
+		cfsubmatch_t	submatch;/* submatch function (direct config) */
+		cfsearch_t	search;	 /* search function (indirect config) */
+	};
+	const char *	iattr;		/* interface attribute */
+	const int *	locators;	/* locators array */
+	devhandle_t	devhandle;	/* devhandle_t (by value) */
+};
+
 static char *number(char *, int);
 static void mapply(struct matchinfo *, cfdata_t);
 static void config_devdelete(device_t);
@@ -177,6 +189,8 @@ static void config_devlink(device_t);
 static void config_alldevs_enter(struct alldevs_foray *);
 static void config_alldevs_exit(struct alldevs_foray *);
 static void config_add_attrib_dict(device_t);
+static device_t	config_attach_internal(device_t, cfdata_t, void *,
+		    cfprint_t, const struct cfargs_internal *);
 
 static void config_collect_garbage(struct devicelist *);
 static void config_dump_garbage(struct devicelist *);
@@ -1068,65 +1082,37 @@ config_probe(device_t parent, cfdata_t c
 	return config_match(parent, cf, aux);
 }
 
-static void
-config_get_cfargs(cfarg_t tag,
-		  cfsubmatch_t *fnp,		/* output */
-		  const char **ifattrp,		/* output */
-		  const int **locsp,		/* output */
-		  devhandle_t *handlep,		/* output */
-		  va_list ap)
-{
-	cfsubmatch_t fn = NULL;
-	const char *ifattr = NULL;
-	const int *locs = NULL;
-	devhandle_t handle;
-
-	devhandle_invalidate(&handle);
-
-	while (tag != CFARG_EOL) {
-		switch (tag) {
-		/*
-		 * CFARG_SUBMATCH and CFARG_SEARCH are synonyms, but this
-		 * is merely an implementation detail.  They are distinct
-		 * from the caller's point of view.
-		 */
-		case CFARG_SUBMATCH:
-		case CFARG_SEARCH:
-			/* Only allow one function to be specified. */
-			if (fn != NULL) {
-				panic("%s: caller specified both "
-				    "SUBMATCH and SEARCH", __func__);
-			}
-			fn = va_arg(ap, cfsubmatch_t);
-			break;
-
-		case CFARG_IATTR:
-			ifattr = va_arg(ap, const char *);
-			break;
+static struct cfargs_internal *
+cfargs_canonicalize(const struct cfargs * const cfargs,
+    struct cfargs_internal * const store)
+{
+	struct cfargs_internal *args = store;
 
-		case CFARG_LOCATORS:
-			locs = va_arg(ap, const int *);
-			break;
+	memset(args, 0, sizeof(*args));
 
-		case CFARG_DEVHANDLE:
-			handle = va_arg(ap, devhandle_t);
-			break;
+	/* If none specified, are all-NULL pointers are good. */
+	if (cfargs == NULL) {
+		return args;
+	}
 
-		default:
-			panic("%s: unknown cfarg tag: %d\n",
-			    __func__, tag);
-		}
-		tag = va_arg(ap, cfarg_t);
+	/*
+	 * submatch and search are mutually-exclusive.
+	 */
+	if (cfargs->submatch != NULL && cfargs->search != NULL) {
+		panic("cfargs_canonicalize: submatch and search are "
+		      "mutually-exclusive");
 	}
+	if (cfargs->submatch != NULL) {
+		args->submatch = cfargs->submatch;
+	} else if (cfargs->search != NULL) {
+		args->search = cfargs->search;
+	}
+
+	args->iattr = cfargs->iattr;
+	args->locators = cfargs->locators;
+	args->devhandle = cfargs->devhandle;
 
-	if (fnp != NULL)
-		*fnp = fn;
-	if (ifattrp != NULL)
-		*ifattrp = ifattr;
-	if (locsp != NULL)
-		*locsp = locs;
-	if (handlep != NULL)
-		*handlep = handle;
+	return args;
 }
 
 /*
@@ -1140,25 +1126,23 @@ config_get_cfargs(cfarg_t tag,
  * an arbitrary function to all potential children (its return value
  * can be ignored).
  */
-cfdata_t
-config_vsearch(device_t parent, void *aux, cfarg_t tag, va_list ap)
+static cfdata_t
+config_search_internal(device_t parent, void *aux,
+    const struct cfargs_internal * const args)
 {
-	cfsubmatch_t fn;
-	const char *ifattr;
-	const int *locs;
 	struct cftable *ct;
 	cfdata_t cf;
 	struct matchinfo m;
 
-	config_get_cfargs(tag, &fn, &ifattr, &locs, NULL, ap);
-
 	KASSERT(config_initialized);
-	KASSERT(!ifattr || cfdriver_get_iattr(parent->dv_cfdriver, ifattr));
-	KASSERT(ifattr || cfdriver_iattr_count(parent->dv_cfdriver) < 2);
+	KASSERT(!args->iattr ||
+		cfdriver_get_iattr(parent->dv_cfdriver, args->iattr));
+	KASSERT(args->iattr ||
+		cfdriver_iattr_count(parent->dv_cfdriver) < 2);
 
-	m.fn = fn;
+	m.fn = args->submatch;		/* N.B. union */
 	m.parent = parent;
-	m.locs = locs;
+	m.locs = args->locators;
 	m.aux = aux;
 	m.match = NULL;
 	m.pri = 0;
@@ -1186,7 +1170,8 @@ config_vsearch(device_t parent, void *au
 			 * consider only children which attach to
 			 * that attribute.
 			 */
-			if (ifattr && !STREQ(ifattr, cfdata_ifattr(cf)))
+			if (args->iattr != NULL &&
+			    !STREQ(args->iattr, cfdata_ifattr(cf)))
 				continue;
 
 			if (cfparent_match(parent, cf->cf_pspec))
@@ -1197,14 +1182,13 @@ config_vsearch(device_t parent, void *au
 }
 
 cfdata_t
-config_search(device_t parent, void *aux, cfarg_t tag, ...)
+config_search(device_t parent, void *aux, const struct cfargs *cfargs)
 {
 	cfdata_t cf;
-	va_list ap;
+	struct cfargs_internal store;
 
-	va_start(ap, tag);
-	cf = config_vsearch(parent, aux, tag, ap);
-	va_end(ap);
+	cf = config_search_internal(parent, aux,
+	    cfargs_canonicalize(cfargs, &store));
 
 	return cf;
 }
@@ -1256,18 +1240,17 @@ static const char * const msgs[] = {
  * not configured, call the given `print' function and return NULL.
  */
 device_t
-config_vfound(device_t parent, void *aux, cfprint_t print, cfarg_t tag,
-    va_list ap)
+config_found(device_t parent, void *aux, cfprint_t print,
+    const struct cfargs * const cfargs)
 {
 	cfdata_t cf;
-	va_list nap;
-
-	va_copy(nap, ap);
-	cf = config_vsearch(parent, aux, tag, nap);
-	va_end(nap);
+	struct cfargs_internal store;
+	const struct cfargs_internal * const args =
+	    cfargs_canonicalize(cfargs, &store);
 
+	cf = config_search_internal(parent, aux, args);
 	if (cf != NULL) {
-		return config_vattach(parent, cf, aux, print, tag, ap);
+		return config_attach_internal(parent, cf, aux, print, args);
 	}
 
 	if (print) {
@@ -1292,19 +1275,6 @@ config_vfound(device_t parent, void *aux
 	return NULL;
 }
 
-device_t
-config_found(device_t parent, void *aux, cfprint_t print, cfarg_t tag, ...)
-{
-	device_t dev;
-	va_list ap;
-
-	va_start(ap, tag);
-	dev = config_vfound(parent, aux, print, tag, ap);
-	va_end(ap);
-
-	return dev;
-}
-
 /*
  * As above, but for root devices.
  */
@@ -1316,7 +1286,7 @@ config_rootfound(const char *rootname, v
 
 	KERNEL_LOCK(1, NULL);
 	if ((cf = config_rootsearch(NULL, rootname, aux)) != NULL)
-		dev = config_attach(ROOT, cf, aux, NULL, CFARG_EOL);
+		dev = config_attach(ROOT, cf, aux, NULL, CFARGS_NONE);
 	else
 		aprint_error("root device %s not configured\n", rootname);
 	KERNEL_UNLOCK_ONE(NULL);
@@ -1539,8 +1509,8 @@ config_unit_alloc(device_t dev, cfdriver
 }
 
 static device_t
-config_vdevalloc(const device_t parent, const cfdata_t cf, cfarg_t tag,
-    va_list ap)
+config_devalloc(const device_t parent, const cfdata_t cf,
+    const struct cfargs_internal * const args)
 {
 	cfdriver_t cd;
 	cfattach_t ca;
@@ -1552,7 +1522,6 @@ config_vdevalloc(const device_t parent, 
 	void *dev_private;
 	const struct cfiattrdata *ia;
 	device_lock_t dvl;
-	const int *locs;
 
 	cd = config_cfdriver_lookup(cf->cf_name);
 	if (cd == NULL)
@@ -1571,12 +1540,7 @@ config_vdevalloc(const device_t parent, 
 	}
 	dev = kmem_zalloc(sizeof(*dev), KM_SLEEP);
 
-	/*
-	 * If a handle was supplied to config_attach(), we'll get it
-	 * assigned automatically here.  If not, then we'll get the
-	 * default invalid handle.
-	 */
-	config_get_cfargs(tag, NULL, NULL, &locs, &dev->dv_handle, ap);
+	dev->dv_handle = args->devhandle;
 
 	dev->dv_class = cd->cd_class;
 	dev->dv_cfdata = cf;
@@ -1598,7 +1562,7 @@ config_vdevalloc(const device_t parent, 
 	xunit = number(&num[sizeof(num)], myunit);
 	lunit = &num[sizeof(num)] - xunit;
 	if (lname + lunit > sizeof(dev->dv_xname))
-		panic("config_vdevalloc: device name too long");
+		panic("config_devalloc: device name too long");
 
 	dvl = device_getlock(dev);
 
@@ -1613,13 +1577,14 @@ config_vdevalloc(const device_t parent, 
 	else
 		dev->dv_depth = 0;
 	dev->dv_flags |= DVF_ACTIVE;	/* always initially active */
-	if (locs) {
+	if (args->locators) {
 		KASSERT(parent); /* no locators at root */
 		ia = cfiattr_lookup(cfdata_ifattr(cf), parent->dv_cfdriver);
 		dev->dv_locators =
 		    kmem_alloc(sizeof(int) * (ia->ci_loclen + 1), KM_SLEEP);
 		*dev->dv_locators++ = sizeof(int) * (ia->ci_loclen + 1);
-		memcpy(dev->dv_locators, locs, sizeof(int) * ia->ci_loclen);
+		memcpy(dev->dv_locators, args->locators,
+		    sizeof(int) * ia->ci_loclen);
 	}
 	dev->dv_properties = prop_dictionary_create();
 	KASSERT(dev->dv_properties != NULL);
@@ -1639,19 +1604,6 @@ config_vdevalloc(const device_t parent, 
 	return dev;
 }
 
-static device_t
-config_devalloc(const device_t parent, const cfdata_t cf, cfarg_t tag, ...)
-{
-	device_t dev;
-	va_list ap;
-
-	va_start(ap, tag);
-	dev = config_vdevalloc(parent, cf, tag, ap);
-	va_end(ap);
-
-	return dev;
-}
-
 /*
  * Create an array of device attach attributes and add it
  * to the device's dv_properties dictionary.
@@ -1734,9 +1686,9 @@ config_add_attrib_dict(device_t dev)
 /*
  * Attach a found device.
  */
-device_t
-config_vattach(device_t parent, cfdata_t cf, void *aux, cfprint_t print,
-    cfarg_t tag, va_list ap)
+static device_t
+config_attach_internal(device_t parent, cfdata_t cf, void *aux, cfprint_t print,
+    const struct cfargs_internal * const args)
 {
 	device_t dev;
 	struct cftable *ct;
@@ -1745,7 +1697,7 @@ config_vattach(device_t parent, cfdata_t
 
 	KASSERT(KERNEL_LOCKED_P());
 
-	dev = config_vdevalloc(parent, cf, tag, ap);
+	dev = config_devalloc(parent, cf, args);
 	if (!dev)
 		panic("config_attach: allocation of device softc failed");
 
@@ -1817,18 +1769,14 @@ config_vattach(device_t parent, cfdata_t
 
 device_t
 config_attach(device_t parent, cfdata_t cf, void *aux, cfprint_t print,
-    cfarg_t tag, ...)
+    const struct cfargs *cfargs)
 {
-	device_t dev;
-	va_list ap;
+	struct cfargs_internal store;
 
 	KASSERT(KERNEL_LOCKED_P());
 
-	va_start(ap, tag);
-	dev = config_vattach(parent, cf, aux, print, tag, ap);
-	va_end(ap);
-
-	return dev;
+	return config_attach_internal(parent, cf, aux, print,
+	    cfargs_canonicalize(cfargs, &store));
 }
 
 /*
@@ -1847,7 +1795,8 @@ config_attach_pseudo(cfdata_t cf)
 
 	KERNEL_LOCK(1, NULL);
 
-	dev = config_devalloc(ROOT, cf, CFARG_EOL);
+	struct cfargs_internal args = { };
+	dev = config_devalloc(ROOT, cf, &args);
 	if (!dev)
 		goto out;
 

Index: src/sys/sys/device.h
diff -u src/sys/sys/device.h:1.171 src/sys/sys/device.h:1.171.2.1
--- src/sys/sys/device.h:1.171	Sat Jun 12 12:13:51 2021
+++ src/sys/sys/device.h	Tue Aug  3 15:00:15 2021
@@ -1,4 +1,4 @@
-/* $NetBSD: device.h,v 1.171 2021/06/12 12:13:51 riastradh Exp $ */
+/* $NetBSD: device.h,v 1.171.2.1 2021/08/03 15:00:15 thorpej Exp $ */
 
 /*
  * Copyright (c) 2021 The NetBSD Foundation, Inc.
@@ -412,6 +412,7 @@ TAILQ_HEAD(cftablelist, cftable);
 #endif
 
 typedef int (*cfsubmatch_t)(device_t, cfdata_t, const int *, void *);
+typedef int (*cfsearch_t)(device_t, cfdata_t, const int *, void *);
 
 /*
  * `configuration' attachment and driver (what the machine-independent
@@ -530,19 +531,39 @@ struct pdevinit {
 #define	DVUNIT_ANY	-1
 
 /*
- * Tags for tag-value argument pairs passed to config_search() and
- * config_found().
+ * Arguments passed to config_search() and config_found().
  */
-typedef enum {
-	CFARG_SUBMATCH		= 0,	/* submatch function (direct config) */
-	CFARG_SEARCH		= 1,	/* search function (indirect config) */
-	CFARG_IATTR		= 2,	/* interface attribute */
-	CFARG_LOCATORS		= 3,	/* locators array */
-	CFARG_DEVHANDLE		= 4,	/* devhandle_t (by value) */
-
-	/* ...a value not likely to occur randomly in the wild. */
-	CFARG_EOL		= 0xeeeeeeee
-} cfarg_t;
+struct cfargs {
+	uintptr_t	cfargs_version;	/* version field */
+
+	/* version 1 fields */
+	cfsubmatch_t	submatch;	/* submatch function (direct config) */
+	cfsearch_t	search;		/* search function (indirect config) */
+	const char *	iattr;		/* interface attribute */
+	const int *	locators;	/* locators array */
+	devhandle_t	devhandle;	/* devhandle_t (by value) */
+
+	/* version 2 fields below here */
+};
+
+#define	CFARGS_VERSION		1	/* current cfargs version */
+
+#define	CFARGS_NONE		NULL	/* no cfargs to pass */
+
+/*
+ * Construct a cfargs with this macro, like so:
+ *
+ *	CFARGS(.submatch = config_stdsubmatch,
+ *	       .devhandle = my_devhandle)
+ *
+ * You must supply at least one field.  If you don't need any, use the
+ * CFARGS_NONE macro.
+ */
+#define	CFARGS(...)							\
+	&((const struct cfargs){					\
+		.cfargs_version = CFARGS_VERSION,			\
+		__VA_ARGS__						\
+	})
 
 #ifdef _KERNEL
 
@@ -587,25 +608,15 @@ const struct cfiattrdata *cfiattr_lookup
 const char *cfdata_ifattr(const struct cfdata *);
 
 int	config_stdsubmatch(device_t, cfdata_t, const int *, void *);
-cfdata_t config_search(device_t, void *, cfarg_t, ...);
+cfdata_t config_search(device_t, void *, const struct cfargs *);
 cfdata_t config_rootsearch(cfsubmatch_t, const char *, void *);
-device_t config_found(device_t, void *, cfprint_t, cfarg_t, ...);
+device_t config_found(device_t, void *, cfprint_t, const struct cfargs *);
 device_t config_rootfound(const char *, void *);
-device_t config_attach(device_t, cfdata_t, void *, cfprint_t, cfarg_t, ...);
+device_t config_attach(device_t, cfdata_t, void *, cfprint_t,
+	    const struct cfargs *);
 int	config_match(device_t, cfdata_t, void *);
 int	config_probe(device_t, cfdata_t, void *);
 
-#if defined(__SUBR_AUTOCONF_PRIVATE)
-/*
- * XXX Some ports abuse the internals of autoconfiguration, so we need
- * XXX provide these symbols to them for the time being.
- */
-cfdata_t config_vsearch(device_t, void *, cfarg_t, va_list);
-device_t config_vfound(device_t, void *, cfprint_t, cfarg_t, va_list);
-device_t config_vattach(device_t, cfdata_t, void *, cfprint_t, cfarg_t,
-			va_list);
-#endif /* __SUBR_AUTOCONF_PRIVATE */
-
 bool	ifattr_match(const char *, const char *);
 
 device_t config_attach_pseudo(cfdata_t);

Reply via email to