Subject: LKM versioning
To: None <tech-kern@netbsd.org>
From: Jaromir Dolecek <jdolecek@netbsd.org>
List: tech-kern
Date: 08/24/2003 16:50:34
--ELM1061736634-16290-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=US-ASCII

Hi,

this is the final version of basic LKM versioning code. The idea
is to detect some cases of incompatibility between to-be-loaded
LKM and kernel, eliminating the old problem with stale LKMs
causing crashes.

The change makes kernel to refuse to load LKM compiled
for different kernel version, or with different setting
of DIAGNOSTIC, DEBUG, LOCKDEBUG and MULTIPROCESSOR flags.
The version checks are extensible, so it's possible
to check e.g. more options or transparently allow
kernel version mismatch for some cases (would be useful
on stable branches, where the kernel structures don't change).

This version, countrary to previous version posted here,
retains source-code compatibility with older LKM interface,
so LKM modules won't normally need any changes to be compilable
under new world. They will need to be recompiled, though.

If there won't be any serious objections to this, I'd commit
this within couple of days.

Appended is diff to sys/ tree.

Jaromir
-- 
Jaromir Dolecek <jdolecek@NetBSD.org>            http://www.NetBSD.cz/
-=- We should be mindful of the potential goal, but as the tantric    -=-
-=- Buddhist masters say, ``You may notice during meditation that you -=-
-=- sometimes levitate or glow.   Do not let this distract you.''     -=-

--ELM1061736634-16290-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=ISO-8859-2
Content-Disposition: attachment; filename=lkmver.diff
Content-Description: 

Index: kern/kern_lkm.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_lkm.c,v
retrieving revision 1.67
diff -u -p -r1.67 kern_lkm.c
--- kern/kern_lkm.c	2003/06/29 22:31:21	1.67
+++ kern/kern_lkm.c	2003/08/24 14:33:57
@@ -269,7 +269,6 @@ lkmioctl(dev, cmd, data, flag, p)
 		}
 		curp = &lkmods[i];
 		curp->id = i;		/* self reference slot offset */
-		curp->ver = LKM_VERSION;
 
 		resrvp->slot = i;		/* return slot */
 
@@ -414,7 +413,7 @@ lkmioctl(dev, cmd, data, flag, p)
 				(*((long *) (data)));
 
 		/* call entry(load)... (assigns "private" portion) */
-		error = (*(curp->entry))(curp, LKM_E_LOAD, curp->ver);
+		error = (*(curp->entry))(curp, LKM_E_LOAD, 0 /* dummy */);
 		if (error) {
 			/*
 			 * Module may refuse loading or may have a
@@ -498,7 +497,7 @@ lkmioctl(dev, cmd, data, flag, p)
 		}
 
 		/* call entry(unload) */
-		if ((*(curp->entry))(curp, LKM_E_UNLOAD, curp->ver)) {
+		if ((*(curp->entry))(curp, LKM_E_UNLOAD, 0 /* dummy */)) {
 			error = EBUSY;
 			break;
 		}
@@ -551,7 +550,7 @@ lkmioctl(dev, cmd, data, flag, p)
 			break;
 		}
 
-		if ((error = (*curp->entry)(curp, LKM_E_STAT, curp->ver)))
+		if ((error = (*curp->entry)(curp, LKM_E_STAT, 0 /* dummy */)))
 			break;
 
 		/*
@@ -563,7 +562,7 @@ lkmioctl(dev, cmd, data, flag, p)
 		statp->area	= curp->area;
 		statp->size	= curp->size / PAGESIZE;
 		statp->private	= (unsigned long)curp->private.lkm_any;
-		statp->ver	= curp->private.lkm_any->lkm_ver;
+		statp->ver	= LKM_VERSION;
 		copystr(curp->private.lkm_any->lkm_name, 
 			  statp->name,
 			  MAXLKMNAME - 2,
@@ -930,4 +929,46 @@ lkmdispatch(lkmtp, cmd)
 	}
 
 	return (error);
+}
+
+/*
+ * Check LKM version against current kernel.
+ */
+int
+lkm_checkver(struct lkm_any *mod, int lkmver, int kernver, int envver,
+	const char *envdep)
+{
+	if (lkmver != LKM_VERSION) {
+		printf("LKM '%s': LKM version mismatch - LKM %d, kernel %d\n",
+		    mod->lkm_name, lkmver, LKM_VERSION);
+		return (1);
+	}
+
+	if (kernver != __NetBSD_Version__) {
+		printf("LKM '%s': kernel version mismatch - LKM %d, kernel %d\n",
+		    mod->lkm_name, kernver, __NetBSD_Version__);
+		return (2);
+	}
+
+	/*
+	 * Following might eventually be changed to take into account envdep,
+	 * if it's non-NULL.
+	 */
+	if (envver != _LKM_ENV_VERSION) {
+		char lbuf[32], kbuf[32];
+
+		bitmask_snprintf(envver, _LKM_ENV_FMT, lbuf, sizeof(lbuf));
+		bitmask_snprintf(_LKM_ENV_VERSION, _LKM_ENV_FMT,
+		    kbuf, sizeof(kbuf));
+
+		printf("LKM '%s': environment options mismatch - LKM '%s', kernel '%s'\n",
+		    mod->lkm_name, lbuf, kbuf);
+		return (3);
+	}
+
+	/*
+	 * Basic parameters match, LKM is likely to be compatible.
+	 * Cross fingers and approve.
+	 */
+	return (0);
 }
Index: sys/lkm.h
===================================================================
RCS file: /cvsroot/src/sys/sys/lkm.h,v
retrieving revision 1.28
diff -u -p -r1.28 lkm.h
--- sys/lkm.h	2003/07/08 06:18:00	1.28
+++ sys/lkm.h	2003/08/24 14:33:57
@@ -53,7 +53,7 @@ typedef enum loadmod {
 	LM_MISC,
 } MODTYPE;
 
-#define	LKM_VERSION	1		/* version of module loader */
+#define	LKM_VERSION	2		/* version of module loader */
 #define	MAXLKMNAME	32
 
 /****************************************************************************/
@@ -140,7 +140,7 @@ struct lkm_compat {
 struct lkm_misc {
 	MODTYPE	lkm_type;
 	int	lkm_ver;
-	char	*lkm_name;
+	const char	*lkm_name;
 	u_long	lkm_offset;
 };
 
@@ -150,7 +150,7 @@ struct lkm_misc {
 struct lkm_any {
 	MODTYPE	lkm_type;
 	int	lkm_ver;
-	char	*lkm_name;
+	const char	*lkm_name;
 	u_long	lkm_offset;
 };
 
@@ -180,7 +180,6 @@ struct lkm_table {
 	u_long	area;
 	char	used;
 
-	int	ver;		/* version (INIT) */
 	int	refcnt;		/* reference count (INIT) */
 	int	depcnt;		/* dependency count (INIT) */
 	int	id;		/* identifier (INIT) */
@@ -258,24 +257,65 @@ struct lkm_table {
 		name				\
 	};
 
+/*
+ * Environment encoding, for LKM<->kernel compatibility check.
+ */
+#ifdef DIAGNOSTIC
+#define _LKM_E_DIAGNOSTIC	1
+#else
+#define _LKM_E_DIAGNOSTIC	0
+#endif
+
+#ifdef DEBUG
+#define _LKM_E_DEBUG	2
+#else
+#define _LKM_E_DEBUG	0
+#endif
+
+#ifdef LOCKDEBUG
+#define _LKM_E_LOCKDEBUG	4
+#else
+#define _LKM_E_LOCKDEBUG	0
+#endif
+
+#ifdef MULTIPROCESSOR
+#define _LKM_E_MULTIPROCESSOR	8
+#else
+#define _LKM_E_MULTIPROCESSOR	0
+#endif
+
+#define	_LKM_ENV_VERSION	\
+	(_LKM_E_DEBUG | _LKM_E_DIAGNOSTIC | _LKM_E_LOCKDEBUG \
+	| _LKM_E_MULTIPROCESSOR)
+#define _LKM_ENV_FMT		\
+	"\20\01DIAGNOSTIC"	\
+	"\02DEBUG"		\
+	"\03LOCKDEBUG"		\
+	"\04MULTIPROCESSOR"		
+	
 
 extern int	lkm_nofunc __P((struct lkm_table *, int));
 extern int	lkmexists __P((struct lkm_table *));
 extern int	lkmdispatch __P((struct lkm_table *, int));
+extern int	lkm_checkver __P((struct lkm_any *, int, int, int, const char *));
 
 /*
- * DISPATCH -- body function for use in module entry point function;
+ * LKM_DISPATCH -- body function for use in module entry point function;
  * generally, the function body will consist entirely of a single
- * DISPATCH line.
+ * LKM_DISPATCH line.
  *
  * If load/unload/stat are called on each corresponding entry instance.
  * If no function is desired for load/stat/unload, lkm_nofunc() should
  * be specified.  "cmd" is passed to each function so that a single
  * function can be used if desired.
  */
-#define	DISPATCH(lkmtp,cmd,ver,load,unload,stat)			\
-	if (ver != LKM_VERSION)						\
-		return EINVAL;	/* version mismatch */		\
+#define LKM_CHECKVER(envdep)	\
+	if (lkm_checkver((void *)&_module, LKM_VERSION,			\
+	    __NetBSD_Version__,	_LKM_ENV_VERSION, envdep))		\
+		return EPROGMISMATCH	/* version mismatch */
+	
+#define	LKM_DISPATCH(lkmtp, envdep, cmd, load, unload, stat)		\
+	LKM_CHECKVER(envdep);						\
 	switch (cmd) {							\
 	int	error;							\
 	case LKM_E_LOAD:						\
@@ -293,6 +333,10 @@ extern int	lkmdispatch __P((struct lkm_t
 		break;							\
 	}								\
 	return lkmdispatch(lkmtp, cmd);
+
+/* remap the old macro for backward source compatibility */
+#define	DISPATCH(lkmtp, cmd, ver, att, det, stat)	\
+	LKM_DISPATCH(lkmtp, NULL, cmd, att, det, stat)
 
 extern struct vm_map *lkm_map;
 void lkm_init(void);
Index: lkm/arch/mac68k/iwm/iwm_mod.c
===================================================================
RCS file: /cvsroot/src/sys/lkm/arch/mac68k/iwm/iwm_mod.c,v
retrieving revision 1.6
diff -u -p -r1.6 iwm_mod.c
--- lkm/arch/mac68k/iwm/iwm_mod.c	2002/09/06 13:23:53	1.6
+++ lkm/arch/mac68k/iwm/iwm_mod.c	2003/08/24 14:33:58
@@ -68,8 +68,7 @@ iwmfd_lkmentry (lkmtp, cmd, ver)
 {
 	int error = 0;
 
-	if (ver != LKM_VERSION)
-		return (EINVAL);
+	LKM_CHECKVER(NULL);
 
 	switch (cmd) {
 	case LKM_E_LOAD:
Index: lkm/vfs/coda/lkminit_vfs.c
===================================================================
RCS file: /cvsroot/src/sys/lkm/vfs/coda/lkminit_vfs.c,v
retrieving revision 1.5
diff -u -p -r1.5 lkminit_vfs.c
--- lkm/vfs/coda/lkminit_vfs.c	2002/12/11 12:13:11	1.5
+++ lkm/vfs/coda/lkminit_vfs.c	2003/08/24 14:33:58
@@ -92,8 +92,8 @@ coda_lkmentry(lkmtp, cmd, ver)
 {
 	int error = 0;
 
-	if (ver != LKM_VERSION)
-		return EINVAL;
+#define _module		coda_lkm_vfs		/* for LKM_CHECKVER() */
+	LKM_CHECKVER(NULL);
 
 	switch (cmd) {
 	case LKM_E_LOAD:
Index: lkm/vfs/ufs/lfs/lkminit_vfs.c
===================================================================
RCS file: /cvsroot/src/sys/lkm/vfs/ufs/lfs/lkminit_vfs.c,v
retrieving revision 1.6
diff -u -p -r1.6 lkminit_vfs.c
--- lkm/vfs/ufs/lfs/lkminit_vfs.c	2003/03/15 07:20:23	1.6
+++ lkm/vfs/ufs/lfs/lkminit_vfs.c	2003/08/24 14:33:58
@@ -103,11 +103,10 @@ lfs_lkmentry(lkmtp, cmd, ver)
 
 	/*
 	 * Since we need to call lkmdispatch() first, we can't use
-	 * DISPATCH().
+	 * LKM_DISPATCH().
 	 */
 
-	if (ver != LKM_VERSION)
-		return EINVAL;	/* version mismatch */
+	LKM_CHECKVER(NULL);
 
 	/* do this first, lkmdispatch() expects this to be already filled in */
 	if (cmd == LKM_E_LOAD)

--ELM1061736634-16290-0_--