[PATCH] Dynamic kernel environment support (kgetenv() and friends), ACPI quirks
Thomas E. Spanjaard
tgen at netphreax.net
Fri Jan 12 11:04:46 PST 2007
Attached patch adds dynamic kernel enviroment support, adapted from
FreeBSD; this allows us to actually be able to use ACPI quirks. As such
I also introduce an ACPI quirk entry for the VMware ACPI-fast24 timer,
which runs like mad.
If noone objects, I'll commit tomorrow.
Cheers,
--
Thomas E. Spanjaard
tgen at netphreax.net
diff -r c63dd4a6b22d sys/conf/options
--- a/sys/conf/options Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/conf/options Fri Jan 12 13:23:27 2007 +0000
@@ -77,6 +77,7 @@ TWA_FLASH_FIRMWARE opt_twa.h
#options for ACPI support
ACPI_DEBUG opt_acpi.h
ACPI_NO_SEMAPHORES opt_acpi.h
+ACPI_QUIRK_VMWARE opt_acpi.h
# Miscellaneous options.
COMPAT_DF12 opt_compatdf12.h
diff -r c63dd4a6b22d sys/dev/acpica5/acpi.c
--- a/sys/dev/acpica5/acpi.c Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/dev/acpica5/acpi.c Sat Jan 20 05:52:40 2007 +0000
@@ -31,6 +31,7 @@
*/
#include "opt_acpi.h"
+
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/proc.h>
@@ -81,6 +82,11 @@ struct mtx acpi_mutex;
struct mtx acpi_mutex;
#endif
+/*
+ * XXX TGEN We should allow for quirks to be, optionally, more specific
+ * regarding hardware they match, e.g. specifying CreatorId and
+ * CreatorRevision.
+ */
struct acpi_quirks {
char *OemId;
uint32_t OemRevision;
@@ -94,7 +100,18 @@ static struct acpi_quirks acpi_quirks_ta
/* Bad PCI routing table. Used on some SuperMicro boards. */
{ "PTLTD ", 0x06040000, "pci_link" },
#endif
-
+#ifdef ACPI_QUIRK_VMWARE
+ /*
+ * VMware's ACPI-fast24 timer runs roughly 65 times too fast, and not
+ * predictably / monotonic either. This is observed at least under SMP
+ * conditions.
+ *
+ * NOTE: this combination of OemId and OemRevision is NOT unique; it's also
+ * known and/or suspected that at least some SuperMicro boards and the
+ * Compaq Presario 1952 use this combination.
+ */
+ { "PTLTD ", 0x06040000, "timer" },
+#endif /* ACPI_QUIRK_VMWARE */
{ NULL, 0, NULL }
};
@@ -652,7 +669,7 @@ acpi_quirks_set(void)
/* XXX No strcasecmp but this is good enough. */
if (*env == 'Y' || *env == 'y')
goto out;
- freeenv(env);
+ kfreeenv(env);
}
if ((env = kgetenv("debug.acpi.disabled")) != NULL) {
if (strstr("quirks", env) != NULL)
@@ -673,9 +690,7 @@ acpi_quirks_set(void)
if ((tmp = kmalloc(len, M_TEMP, M_NOWAIT)) == NULL)
goto out;
ksprintf(tmp, "%s %s", env ? env : "", quirk->value);
-#ifdef notyet
- setenv("debug.acpi.disabled", tmp);
-#endif /* notyet */
+ ksetenv("debug.acpi.disabled", tmp);
kfree(tmp, M_TEMP);
break;
}
@@ -683,7 +698,7 @@ acpi_quirks_set(void)
out:
if (env)
- freeenv(env);
+ kfreeenv(env);
}
/*
diff -r c63dd4a6b22d sys/dev/acpica5/acpi_timer.c
--- a/sys/dev/acpica5/acpi_timer.c Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/dev/acpica5/acpi_timer.c Fri Jan 12 13:21:16 2007 +0000
@@ -27,7 +27,9 @@
* $FreeBSD: src/sys/dev/acpica/acpi_timer.c,v 1.33 2004/05/30 20:08:23 phk Exp $
* $DragonFly: src/sys/dev/acpica5/acpi_timer.c,v 1.11 2006/12/22 23:26:14 swildner Exp $
*/
+
#include "opt_acpi.h"
+
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
diff -r c63dd4a6b22d sys/kern/kern_environment.c
--- a/sys/kern/kern_environment.c Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/kern/kern_environment.c Mon Jan 22 08:03:13 2007 +0000
@@ -29,23 +29,91 @@
/*
* The unified bootloader passes us a pointer to a preserved copy of
- * bootstrap/kernel environment variables.
+ * bootstrap/kernel environment variables. We convert them to a dynamic array
+ * of strings later when the VM subsystem is up.
* We make these available using sysctl for both in-kernel and
- * out-of-kernel consumers.
+ * out-of-kernel consumers, as well as the k{get,set,unset,test,free}env()
+ * functions for in-kernel consumers.
*
* Note that the current sysctl infrastructure doesn't allow
* dynamic insertion or traversal through handled spaces. Grr.
+ *
+ * Note also that the dynamic kernel environment space is not yet exported via
+ * sysctl. It should be handled somewhat like kinfo.
*/
#include <sys/param.h>
#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/malloc.h>
+#include <sys/spinlock.h>
+#include <sys/spinlock2.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
-#include <sys/libkern.h>
-
-char *kern_envp;
-
-static char *kernenv_next(char *cp);
+
+/* exported variables */
+char *kern_envp; /* sys/systm.h */
+
+/* local variables */
+char **kenv_dynp;
+int kenv_isdynamic;
+struct spinlock kenv_dynlock;
+
+/* constants */
+MALLOC_DEFINE(M_KENV, "kenv", "kernel environment dynamic storage");
+#define KENV_DYNMAXNUM 512
+#define KENV_MAXNAMELEN 128
+#define KENV_MAXVALLEN 128
+
+/* local prototypes */
+static char *kenv_getstring_dynamic(const char *, int *);
+static char *kenv_getstring_static(const char*);
+static char *kernenv_next(char *);
+
+/*
+ * Look up a string in the dynamic environment array.
+ *
+ * Must be called with kenv_dynlock held.
+ */
+static char *
+kenv_getstring_dynamic(const char *name, int *idx)
+{
+ char *cp;
+ int len, i;
+
+ len = strlen(name);
+ /* note: kunsetenv() never leaves gaps in the array */
+ for (cp = kenv_dynp[0], i = 0; cp != NULL; cp = kenv_dynp[i++]) {
+ if ((strncmp(cp, name, len) == 0) && (cp[len] == '=')) {
+ if (idx != NULL)
+ *idx = i;
+ return(cp + len + 1);
+ }
+ }
+ return(NULL);
+}
+
+/*
+ * Look up a string in the static environment array.
+ */
+static char *
+kenv_getstring_static(const char *name)
+{
+ char *cp, *ep;
+ int len;
+
+ for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
+ for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
+ ;
+ if (*ep != '=')
+ continue;
+ len = ep - cp;
+ ep++;
+ if (!strncmp(name, cp, len) && name[len] == 0)
+ return(ep);
+ }
+ return(NULL);
+}
/*
* Look up an environment variable by name.
@@ -53,20 +121,132 @@ char *
char *
kgetenv(const char *name)
{
- char *cp, *ep;
- int len;
-
- for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
- for (ep = cp; (*ep != '=') && (*ep != 0); ep++)
- ;
- if (*ep != '=')
- continue;
- len = ep - cp;
- ep++;
- if (!strncmp(name, cp, len) && name[len] == 0)
- return(ep);
- }
- return(NULL);
+ char buf[KENV_MAXNAMELEN + 1 + KENV_MAXVALLEN + 1];
+ char *ret, *cp;
+ int len;
+
+ if (kenv_isdynamic) {
+ spin_lock_wr(&kenv_dynlock);
+ cp = kenv_getstring_dynamic(name, NULL);
+ if (cp != NULL) {
+ strcpy(buf, cp);
+ spin_unlock_wr(&kenv_dynlock);
+ len = strlen(buf) + 1;
+ ret = kmalloc(len, M_KENV, M_WAITOK);
+ strcpy(ret, buf);
+ } else {
+ spin_unlock_wr(&kenv_dynlock);
+ ret = NULL;
+ }
+ } else
+ ret = kenv_getstring_static(name);
+ return(ret);
+}
+
+/*
+ * Set an environment variable by name.
+ */
+int
+ksetenv(const char *name, const char *value)
+{
+ char *buf, *cp, *oldenv;
+ int namelen, vallen, i;
+
+ if (kenv_isdynamic) {
+ namelen = strlen(name) + 1;
+ vallen = strlen(value) + 1;
+ if ((namelen > KENV_MAXNAMELEN) || (vallen > KENV_MAXVALLEN))
+ return(-1);
+ buf = kmalloc(namelen + vallen, M_KENV, M_WAITOK);
+ ksprintf(buf, "%s=%s", name, value);
+
+ spin_lock_wr(&kenv_dynlock);
+ cp = kenv_getstring_dynamic(name, &i);
+ if (cp != NULL) {
+ /* replace existing environment variable */
+ oldenv = kenv_dynp[i];
+ kenv_dynp[i] = buf;
+ spin_unlock_wr(&kenv_dynlock);
+ kfree(oldenv, M_KENV);
+ } else {
+ /* new environment variable: find first free slot */
+ for (i = 0; (cp = kenv_dynp[i]) != NULL; i++)
+ ;
+
+ /* bounds checking */
+ if (i < 0 || i >= (KENV_DYNMAXNUM - 1)) {
+ kfree(buf, M_KENV);
+ spin_unlock_wr(&kenv_dynlock);
+ return(-1);
+ }
+
+ kenv_dynp[i] = buf;
+ kenv_dynp[i + 1] = NULL;
+ spin_unlock_wr(&kenv_dynlock);
+ }
+ return(0);
+ } else {
+ kprintf("WARNING: ksetenv: dynamic array not created yet\n");
+ return(-1);
+ }
+}
+
+/*
+ * Unset an environment variable by name.
+ */
+int
+kunsetenv(const char *name)
+{
+ char *cp, *oldenv;
+ int i, j;
+
+ if (kenv_isdynamic) {
+ spin_lock_wr(&kenv_dynlock);
+ cp = kenv_getstring_dynamic(name, &i);
+ if (cp != NULL) {
+ oldenv = kenv_dynp[i];
+ for (j = i + 1; kenv_dynp[j] != NULL; j++)
+ kenv_dynp[i++] = kenv_dynp[j];
+ kenv_dynp[i] = NULL;
+ spin_unlock_wr(&kenv_dynlock);
+ kfree(oldenv, M_KENV);
+ return(0);
+ }
+ spin_unlock_wr(&kenv_dynlock);
+ return(-1);
+ } else {
+ kprintf("WARNING: kunsetenv: dynamic array not created yet\n");
+ return(-1);
+ }
+}
+
+/*
+ * Free an environment variable that has been copied for a consumer.
+ */
+void
+kfreeenv(char *env)
+{
+ if (kenv_isdynamic)
+ kfree(env, M_KENV);
+}
+
+/*
+ * Test if an environment variable is defined.
+ */
+int
+ktestenv(const char *name)
+{
+ char *cp;
+
+ if (kenv_isdynamic) {
+ spin_lock_wr(&kenv_dynlock);
+ cp = kenv_getstring_dynamic(name, NULL);
+ spin_unlock_wr(&kenv_dynlock);
+ } else
+ cp = kenv_getstring_static(name);
+ if (cp != NULL)
+ return(1);
+ return(0);
}
/*
@@ -81,6 +261,7 @@ kgetenv_string(const char *name, char *d
if (tmp != NULL) {
strncpy(data, tmp, size);
data[size - 1] = 0;
+ kfreeenv(tmp);
return (1);
} else
return (0);
@@ -116,15 +297,21 @@ kgetenv_quad(const char *name, quad_t *d
return(0);
iv = strtoq(value, &vtp, 0);
- if ((vtp == value) || (*vtp != '\0'))
+ if ((vtp == value) || (*vtp != '\0')) {
+ kfreeenv(value);
return(0);
+ }
*data = iv;
+ kfreeenv(value);
return(1);
}
+/*
+ * Boottime kernel environment sysctl handler.
+ */
static int
-sysctl_kernenv(SYSCTL_HANDLER_ARGS)
+sysctl_kenv_boot(SYSCTL_HANDLER_ARGS)
{
int *name = (int *)arg1;
u_int namelen = arg2;
@@ -153,8 +340,46 @@ sysctl_kernenv(SYSCTL_HANDLER_ARGS)
error = SYSCTL_OUT(req, cp, strlen(cp) + 1);
return (error);
}
-
-SYSCTL_NODE(_kern, OID_AUTO, environment, CTLFLAG_RD, sysctl_kernenv, "kernel environment space");
+SYSCTL_NODE(_kern, OID_AUTO, environment, CTLFLAG_RD, sysctl_kenv_boot,
+ "kernel boot environment space");
+
+#ifdef notyet
+/*
+ * Runtime kernel environment sysctl handler.
+ */
+static int
+sysctl_kenv(SYSCTL_HANDLER_ARGS)
+{
+ int *name = (int *)arg1;
+ u_int namelen = arg2;
+ char *bufp;
+ ptr_t buf;
+ int total, i, len;
+
+ if (kenv_isdynamic) {
+ spin_lock_wr(&kenv_dynlock);
+ if (oidp->oid_number == KERN_ENV_ALL) {
+ total = 0;
+ for (i = 0; kenv_dynp[i] != NULL; i++) {
+ len = strlen(kenv_dynp[i]) + 1;
+ total += len;
+ }
+ bufp = kmalloc(total, M_KENV, M_WAITOK);
+ buf = bufp
+ for (i = 0; kenv_dynp[i] != NULL; i++) {
+ len = strlen(kenv_dynp[i]) + 1;
+ strcpy(buf, kenv_dynp[i]);
+ buf += len;
+ }
+ error = SYSCTL_OUT(req, bufp, total);
+ return(error);
+ }
+ }
+ return(-1);
+}
+SYSCTL_NODE(_kern, OID_AUTO, env, CTLFLAG_RD, sysctl_kenv,
+ "kernel runtime environment space");
+#endif /* notyet */
/*
* Find the next entry after the one which (cp) falls within, return a
@@ -197,3 +422,54 @@ tunable_str_init(void *data)
TUNABLE_STR_FETCH(d->path, d->var, d->size);
}
+/*
+ * Create the dynamic kenv stuff, and copy in the static kenv as passed by the
+ * bootloader.
+ */
+static void
+kenv_init(void *dummy)
+{
+ char *cp;
+ int len, i;
+
+ kenv_dynp = kmalloc(KENV_DYNMAXNUM * sizeof(char *), M_KENV,
+ M_WAITOK | M_ZERO);
+
+ /* copy the static kenv to our dynamic kenv */
+ i = 0;
+ for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
+ len = strlen(cp) + 1;
+ if (i < (KENV_DYNMAXNUM - 1)) {
+ kenv_dynp[i] = kmalloc(len, M_KENV, M_WAITOK);
+ strcpy(kenv_dynp[i++], cp);
+ } else
+ kprintf("WARNING: kenv: exhausted dynamic storage, "
+ "ignoring string %s\n", cp);
+ }
+ kenv_dynp[i] = NULL;
+
+ spin_init(&kenv_dynlock);
+ kenv_isdynamic = 1;
+}
+SYSINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, kenv_init, NULL);
+
+/*
+ * Destroy the dynamic kenv stuff.
+ */
+static void
+kenv_uninit(void *dummy)
+{
+ int i;
+
+ if (kenv_isdynamic) {
+ spin_lock_wr(&kenv_dynlock);
+ /* destroy our environment variables */
+ for (i = 0; kenv_dynp[i] != NULL; i++)
+ kfree(kenv_dynp[i], M_KENV);
+ kenv_isdynamic = 0;
+ kfree(kenv_dynp, M_KENV);
+ spin_unlock_wr(&kenv_dynlock);
+ spin_uninit(&kenv_dynlock);
+ }
+}
+SYSUNINIT(kenv, SI_SUB_KMEM, SI_ORDER_ANY, kenv_uninit, NULL);
diff -r c63dd4a6b22d sys/sys/systm.h
--- a/sys/sys/systm.h Wed Jan 10 06:01:06 2007 +0000
+++ b/sys/sys/systm.h Sat Jan 20 04:23:52 2007 +0000
@@ -223,9 +223,19 @@ void cons_lock(void);
void cons_lock(void);
void cons_unlock(void);
+/*
+ * Kernel environment support functions and sundry.
+ *
+ * XXX TGEN Fix all calls to freeenv() and testenv() to their k- counterparts,
+ * so that these compat defines can go the way of the bit bucket.
+ */
char *kgetenv (const char *name);
-#define testenv kgetenv
-#define freeenv(p)
+int ksetenv (const char *name, const char *value);
+int kunsetenv (const char *name);
+void kfreeenv(char *env);
+#define freeenv kfreeenv
+int ktestenv(const char *name);
+#define testenv ktestenv
int kgetenv_int (const char *name, int *data);
int kgetenv_string (const char *name, char *data, int size);
int kgetenv_quad (const char *name, quad_t *data);
Attachment:
signature.asc
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pgp00006.pgp
Type: application/octet-stream
Size: 186 bytes
Desc: "Description: OpenPGP digital signature"
URL: <http://lists.dragonflybsd.org/pipermail/kernel/attachments/20070112/e6ae7a2b/attachment-0019.obj>
More information about the Kernel
mailing list