diff mbox series

[v3,9/9] xen: add runtime parameter access support to hypfs

Message ID 20200121084330.18309-10-jgross@suse.com (mailing list archive)
State Superseded
Headers show
Series Add hypervisor sysfs-like support | expand

Commit Message

Jürgen Groß Jan. 21, 2020, 8:43 a.m. UTC
Add support to read and modify values of hypervisor runtime parameters
via the hypervisor file system.

As runtime parameters can be modified via a sysctl, too, this path has
to take the hypfs rw_lock as writer.

For custom runtime parameters the resulting parameter value needs to
be stored in a string buffer for being consumable by the file system.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V3:
- complete rework
- support custom parameters, too
- support parameter writing
---
 docs/misc/hypfs-paths.pandoc |  9 ++++++
 xen/arch/arm/xen.lds.S       |  5 +++
 xen/arch/x86/hvm/vmx/vmcs.c  | 17 +++++++++-
 xen/arch/x86/pv/domain.c     |  7 ++++-
 xen/arch/x86/xen.lds.S       |  5 +++
 xen/common/grant_table.c     | 17 +++++++---
 xen/common/hypfs.c           | 38 +++++++++++++++++++++++
 xen/common/kernel.c          | 23 +++++++++++++-
 xen/drivers/char/console.c   | 49 +++++++++++++++++++++++++----
 xen/include/xen/hypfs.h      |  4 +++
 xen/include/xen/param.h      | 74 +++++++++++++++++++++++++++++++++++++++-----
 11 files changed, 226 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/docs/misc/hypfs-paths.pandoc b/docs/misc/hypfs-paths.pandoc
index e8ae52ea45..1554039e46 100644
--- a/docs/misc/hypfs-paths.pandoc
+++ b/docs/misc/hypfs-paths.pandoc
@@ -142,3 +142,12 @@  The major version of Xen.
 #### /buildinfo/version/minor = INTEGER
 
 The minor version of Xen.
+
+#### /params/
+
+A directory of runtime parameters.
+
+#### /params/*
+
+The single parameters. The description of the different parameters can be
+found in `docs/misc/xen-command-line.pandoc`.
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index a497f6a48d..0061a8dfea 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -89,6 +89,11 @@  SECTIONS
        __start_schedulers_array = .;
        *(.data.schedulers)
        __end_schedulers_array = .;
+
+       . = ALIGN(8);
+       __paramhypfs_start = .;
+       *(.data.paramhypfs)
+       __paramhypfs_end = .;
        *(.data.rel)
        *(.data.rel.*)
        CONSTRUCTORS
diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c
index 65445afeb0..36ddccfb73 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -70,6 +70,17 @@  integer_param("ple_window", ple_window);
 static bool __read_mostly opt_ept_pml = true;
 static s8 __read_mostly opt_ept_ad = -1;
 int8_t __read_mostly opt_ept_exec_sp = -1;
+static char opt_ept_setting[16] = "pml=1";
+
+
+static void update_ept_param(void)
+{
+    snprintf(opt_ept_setting, sizeof(opt_ept_setting), "pml=%d", opt_ept_pml);
+    if ( opt_ept_ad >= 0 )
+        param_append_str(opt_ept_setting, ",ad=%d", opt_ept_ad);
+    if ( opt_ept_exec_sp >= 0 )
+        param_append_str(opt_ept_setting, ",exec-sp=%d", opt_ept_exec_sp);
+}
 
 static int __init parse_ept_param(const char *s)
 {
@@ -93,6 +104,8 @@  static int __init parse_ept_param(const char *s)
         s = ss + 1;
     } while ( *ss );
 
+    update_ept_param();
+
     return rc;
 }
 custom_param("ept", parse_ept_param);
@@ -115,6 +128,8 @@  static int parse_ept_param_runtime(const char *s)
 
     opt_ept_exec_sp = val;
 
+    update_ept_param();
+
     rcu_read_lock(&domlist_read_lock);
     for_each_domain ( d )
     {
@@ -144,7 +159,7 @@  static int parse_ept_param_runtime(const char *s)
 
     return 0;
 }
-custom_runtime_only_param("ept", parse_ept_param_runtime);
+custom_runtime_only_param("ept", parse_ept_param_runtime, opt_ept_setting);
 
 /* Dynamic (run-time adjusted) execution control flags. */
 u32 vmx_pin_based_exec_control __read_mostly;
diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c
index c3473b9a47..9e0d24ce81 100644
--- a/xen/arch/x86/pv/domain.c
+++ b/xen/arch/x86/pv/domain.c
@@ -22,6 +22,7 @@  static __read_mostly enum {
     PCID_XPTI,
     PCID_NOXPTI
 } opt_pcid = PCID_XPTI;
+static char opt_pcid_val[7] = "xpti";
 
 static int parse_pcid(const char *s)
 {
@@ -31,10 +32,12 @@  static int parse_pcid(const char *s)
     {
     case 0:
         opt_pcid = PCID_OFF;
+        snprintf(opt_pcid_val, sizeof(opt_pcid_val), "off");
         break;
 
     case 1:
         opt_pcid = PCID_ALL;
+        snprintf(opt_pcid_val, sizeof(opt_pcid_val), "on");
         break;
 
     default:
@@ -42,10 +45,12 @@  static int parse_pcid(const char *s)
         {
         case 0:
             opt_pcid = PCID_NOXPTI;
+            snprintf(opt_pcid_val, sizeof(opt_pcid_val), "noxpti");
             break;
 
         case 1:
             opt_pcid = PCID_XPTI;
+            snprintf(opt_pcid_val, sizeof(opt_pcid_val), "xpti");
             break;
 
         default:
@@ -57,7 +62,7 @@  static int parse_pcid(const char *s)
 
     return rc;
 }
-custom_runtime_param("pcid", parse_pcid);
+custom_runtime_param("pcid", parse_pcid, opt_pcid_val);
 
 static void noreturn continue_nonidle_domain(struct vcpu *v)
 {
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 29ef507432..9b717602db 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -280,6 +280,11 @@  SECTIONS
        __start_schedulers_array = .;
        *(.data.schedulers)
        __end_schedulers_array = .;
+
+       . = ALIGN(8);
+       __paramhypfs_start = .;
+       *(.data.paramhypfs)
+       __paramhypfs_end = .;
   } :text
 
   DECL_SECTION(.data) {
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 2ecf38dfbe..2de2c7e373 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -85,8 +85,10 @@  struct grant_table {
     struct grant_table_arch arch;
 };
 
+#define PAR_VAL_SZ 12
+
 static int parse_gnttab_limit(const char *param, const char *arg,
-                              unsigned int *valp)
+                              unsigned int *valp, char *par_val)
 {
     const char *e;
     unsigned long val;
@@ -99,28 +101,33 @@  static int parse_gnttab_limit(const char *param, const char *arg,
         return -ERANGE;
 
     *valp = val;
+    snprintf(par_val, PAR_VAL_SZ, "%lu", val);
 
     return 0;
 }
 
 unsigned int __read_mostly opt_max_grant_frames = 64;
+static char gnttab_max_frames_val[PAR_VAL_SZ] = "64";
 
 static int parse_gnttab_max_frames(const char *arg)
 {
     return parse_gnttab_limit("gnttab_max_frames", arg,
-                              &opt_max_grant_frames);
+                              &opt_max_grant_frames, gnttab_max_frames_val);
 }
-custom_runtime_param("gnttab_max_frames", parse_gnttab_max_frames);
+custom_runtime_param("gnttab_max_frames", parse_gnttab_max_frames,
+                     gnttab_max_frames_val);
 
 static unsigned int __read_mostly opt_max_maptrack_frames = 1024;
+static char max_maptrack_frames_val[PAR_VAL_SZ] = "1024";
 
 static int parse_gnttab_max_maptrack_frames(const char *arg)
 {
     return parse_gnttab_limit("gnttab_max_maptrack_frames", arg,
-                              &opt_max_maptrack_frames);
+                              &opt_max_maptrack_frames,
+                              max_maptrack_frames_val);
 }
 custom_runtime_param("gnttab_max_maptrack_frames",
-                     parse_gnttab_max_maptrack_frames);
+                     parse_gnttab_max_maptrack_frames, max_maptrack_frames_val);
 
 #ifndef GNTTAB_MAX_VERSION
 #define GNTTAB_MAX_VERSION 2
diff --git a/xen/common/hypfs.c b/xen/common/hypfs.c
index 6762d20dfd..667636d63e 100644
--- a/xen/common/hypfs.c
+++ b/xen/common/hypfs.c
@@ -10,6 +10,7 @@ 
 #include <xen/guest_access.h>
 #include <xen/hypercall.h>
 #include <xen/hypfs.h>
+#include <xen/param.h>
 #include <xen/rwlock.h>
 #include <public/hypfs.h>
 
@@ -296,6 +297,33 @@  int hypfs_write_bool(struct hypfs_entry_leaf *leaf,
     return 0;
 }
 
+int hypfs_write_custom(struct hypfs_entry_leaf *leaf,
+                       XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen)
+{
+    struct param_hypfs *p;
+    char *buf;
+    int ret;
+
+    buf = xzalloc_array(char, ulen);
+    if ( !buf )
+        return -ENOMEM;
+
+    ret = -EFAULT;
+    if ( copy_from_guest(buf, uaddr, ulen) )
+        goto out;
+
+    ret = -EDOM;
+    if ( buf[ulen - 1] )
+        goto out;
+
+    p = container_of(leaf, struct param_hypfs, hypfs);
+    ret = p->param->par.func(buf);
+
+ out:
+    xfree(buf);
+    return ret;
+}
+
 static int hypfs_write(struct hypfs_entry *entry,
                        XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen)
 {
@@ -363,3 +391,13 @@  long do_hypfs_op(unsigned int cmd,
 
     return ret;
 }
+
+void hypfs_write_lock(void)
+{
+    write_lock(&hypfs_lock);
+}
+
+void hypfs_write_unlock(void)
+{
+    write_unlock(&hypfs_lock);
+}
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index b5ee3011f5..e9a501e0e1 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -198,7 +198,13 @@  static void __init _cmdline_parse(const char *cmdline)
 
 int runtime_parse(const char *line)
 {
-    return parse_params(line, __param_start, __param_end);
+    int ret;
+
+    hypfs_write_lock();
+    ret = parse_params(line, __param_start, __param_end);
+    hypfs_write_unlock();
+
+    return ret;
 }
 
 /**
@@ -428,6 +434,21 @@  static int __init buildinfo_init(void)
 }
 __initcall(buildinfo_init);
 
+static HYPFS_DIR_INIT(params, "params");
+
+static int __init param_init(void)
+{
+    struct param_hypfs *param;
+
+    hypfs_add_dir(&hypfs_root, &params, true);
+
+    for ( param = __paramhypfs_start; param < __paramhypfs_end; param++ )
+        hypfs_add_leaf(&params, &param->hypfs, true);
+
+    return 0;
+}
+__initcall(param_init);
+
 # define DO(fn) long do_##fn
 
 #endif
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 913ae1b66a..5440145549 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -78,9 +78,11 @@  enum con_timestamp_mode
 };
 
 static enum con_timestamp_mode __read_mostly opt_con_timestamp_mode = TSM_NONE;
+static char con_timestamp_mode_val[7] = "none";
 
 static int parse_console_timestamps(const char *s);
-custom_runtime_param("console_timestamps", parse_console_timestamps);
+custom_runtime_param("console_timestamps", parse_console_timestamps,
+                     con_timestamp_mode_val);
 
 /* conring_size: allows a large console ring than default (16kB). */
 static uint32_t __initdata opt_conring_size;
@@ -118,13 +120,17 @@  static DEFINE_SPINLOCK(console_lock);
 #ifdef NDEBUG
 #define XENLOG_UPPER_THRESHOLD       2 /* Do not print INFO and DEBUG  */
 #define XENLOG_LOWER_THRESHOLD       2 /* Always print ERR and WARNING */
+#define XENLOG_DEFAULT_VAL           "warning/warning"
 #define XENLOG_GUEST_UPPER_THRESHOLD 2 /* Do not print INFO and DEBUG  */
 #define XENLOG_GUEST_LOWER_THRESHOLD 0 /* Rate-limit ERR and WARNING   */
+#define XENLOG_GUEST_DEFAULT_VAL     "none/warning"
 #else
 #define XENLOG_UPPER_THRESHOLD       4 /* Do not discard anything      */
 #define XENLOG_LOWER_THRESHOLD       4 /* Print everything             */
+#define XENLOG_DEFAULT_VAL           "all/all"
 #define XENLOG_GUEST_UPPER_THRESHOLD 4 /* Do not discard anything      */
 #define XENLOG_GUEST_LOWER_THRESHOLD 4 /* Print everything             */
+#define XENLOG_GUEST_DEFAULT_VAL     "all/all"
 #endif
 /*
  * The XENLOG_DEFAULT is the default given to printks that
@@ -133,16 +139,20 @@  static DEFINE_SPINLOCK(console_lock);
 #define XENLOG_DEFAULT       1 /* XENLOG_WARNING */
 #define XENLOG_GUEST_DEFAULT 1 /* XENLOG_WARNING */
 
+#define LOGLVL_VAL_SZ 16
 static int __read_mostly xenlog_upper_thresh = XENLOG_UPPER_THRESHOLD;
 static int __read_mostly xenlog_lower_thresh = XENLOG_LOWER_THRESHOLD;
+static char xenlog_val[LOGLVL_VAL_SZ] = XENLOG_DEFAULT_VAL;
 static int __read_mostly xenlog_guest_upper_thresh =
     XENLOG_GUEST_UPPER_THRESHOLD;
 static int __read_mostly xenlog_guest_lower_thresh =
     XENLOG_GUEST_LOWER_THRESHOLD;
+static char xenlog_guest_val[LOGLVL_VAL_SZ] = XENLOG_GUEST_DEFAULT_VAL;
 
 static int parse_loglvl(const char *s);
 static int parse_guest_loglvl(const char *s);
 
+static char *lvl2opt[] = { "none", "error", "warning", "info", "all" };
 /*
  * <lvl> := none|error|warning|info|debug|all
  * loglvl=<lvl_print_always>[/<lvl_print_ratelimit>]
@@ -151,8 +161,8 @@  static int parse_guest_loglvl(const char *s);
  * Similar definitions for guest_loglvl, but applies to guest tracing.
  * Defaults: loglvl=warning ; guest_loglvl=none/warning
  */
-custom_runtime_param("loglvl", parse_loglvl);
-custom_runtime_param("guest_loglvl", parse_guest_loglvl);
+custom_runtime_param("loglvl", parse_loglvl, xenlog_val);
+custom_runtime_param("guest_loglvl", parse_guest_loglvl, xenlog_guest_val);
 
 static atomic_t print_everything = ATOMIC_INIT(0);
 
@@ -173,7 +183,7 @@  static int __parse_loglvl(const char *s, const char **ps)
     return 2; /* sane fallback */
 }
 
-static int _parse_loglvl(const char *s, int *lower, int *upper)
+static int _parse_loglvl(const char *s, int *lower, int *upper, char *val)
 {
     *lower = *upper = __parse_loglvl(s, &s);
     if ( *s == '/' )
@@ -181,18 +191,21 @@  static int _parse_loglvl(const char *s, int *lower, int *upper)
     if ( *upper < *lower )
         *upper = *lower;
 
+    snprintf(val, LOGLVL_VAL_SZ, "%s/%s", lvl2opt[*lower], lvl2opt[*upper]);
+
     return *s ? -EINVAL : 0;
 }
 
 static int parse_loglvl(const char *s)
 {
-    return _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh);
+    return _parse_loglvl(s, &xenlog_lower_thresh, &xenlog_upper_thresh,
+                         xenlog_val);
 }
 
 static int parse_guest_loglvl(const char *s)
 {
     return _parse_loglvl(s, &xenlog_guest_lower_thresh,
-                         &xenlog_guest_upper_thresh);
+                         &xenlog_guest_upper_thresh, xenlog_guest_val);
 }
 
 static char *loglvl_str(int lvl)
@@ -731,22 +744,46 @@  static int parse_console_timestamps(const char *s)
     {
     case 0:
         opt_con_timestamp_mode = TSM_NONE;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "none");
         return 0;
     case 1:
         opt_con_timestamp_mode = TSM_DATE;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "date");
         return 0;
     }
     if ( *s == '\0' || /* Compat for old booleanparam() */
          !strcmp(s, "date") )
+    {
         opt_con_timestamp_mode = TSM_DATE;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "date");
+    }
     else if ( !strcmp(s, "datems") )
+    {
         opt_con_timestamp_mode = TSM_DATE_MS;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                "datems");
+    }
     else if ( !strcmp(s, "boot") )
+    {
         opt_con_timestamp_mode = TSM_BOOT;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "boot");
+    }
     else if ( !strcmp(s, "raw") )
+    {
         opt_con_timestamp_mode = TSM_RAW;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "raw");
+    }
     else if ( !strcmp(s, "none") )
+    {
         opt_con_timestamp_mode = TSM_NONE;
+        snprintf(con_timestamp_mode_val, sizeof(con_timestamp_mode_val),
+                 "none");
+    }
     else
         return -EINVAL;
 
diff --git a/xen/include/xen/hypfs.h b/xen/include/xen/hypfs.h
index a1c2140398..d1ea095dc1 100644
--- a/xen/include/xen/hypfs.h
+++ b/xen/include/xen/hypfs.h
@@ -85,5 +85,9 @@  int hypfs_write_leaf(struct hypfs_entry_leaf *leaf,
                      XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen);
 int hypfs_write_bool(struct hypfs_entry_leaf *leaf,
                      XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen);
+int hypfs_write_custom(struct hypfs_entry_leaf *leaf,
+                       XEN_GUEST_HANDLE_PARAM(void) uaddr, unsigned long ulen);
+void hypfs_write_lock(void);
+void hypfs_write_unlock(void);
 
 #endif /* __XEN_HYPFS_H__ */
diff --git a/xen/include/xen/param.h b/xen/include/xen/param.h
index 75471eb4ad..b4ef3e7a22 100644
--- a/xen/include/xen/param.h
+++ b/xen/include/xen/param.h
@@ -1,6 +1,7 @@ 
 #ifndef _XEN_PARAM_H
 #define _XEN_PARAM_H
 
+#include <xen/hypfs.h>
 #include <xen/init.h>
 
 /*
@@ -23,10 +24,17 @@  struct kernel_param {
     } par;
 };
 
+struct param_hypfs {
+    const struct kernel_param *param;
+    struct hypfs_entry_leaf hypfs;
+};
+
 extern const struct kernel_param __setup_start[], __setup_end[];
 extern const struct kernel_param __param_start[], __param_end[];
+extern struct param_hypfs __paramhypfs_start[], __paramhypfs_end[];
 
 #define __dataparam       __used_section(".data.param")
+#define __paramhypfs      __used_section(".data.paramhypfs")
 
 #define __param(att)      static const att \
     __attribute__((__aligned__(sizeof(void *)))) struct kernel_param
@@ -76,40 +84,87 @@  extern const struct kernel_param __param_start[], __param_end[];
           .type = OPT_IGNORE }
 
 #define __rtparam         __param(__dataparam)
+#define __paramfs         static __paramhypfs \
+    __attribute__((__aligned__(sizeof(void *)))) struct param_hypfs
 
-#define custom_runtime_only_param(_name, _var) \
+#define custom_runtime_only_param(_name, _var, contvar) \
     __rtparam __rtpar_##_var = \
       { .name = _name, \
           .type = OPT_CUSTOM, \
-          .par.func = _var }
+          .par.func = _var }; \
+    __paramfs __parfs_##_var = \
+        { .param = &__rtpar_##_var, \
+          .hypfs.e.type = XEN_HYPFS_TYPE_STRING, \
+          .hypfs.e.encoding = XEN_HYPFS_ENC_PLAIN, \
+          .hypfs.e.name = _name, \
+          .hypfs.e.size = sizeof(contvar), \
+          .hypfs.e.read = hypfs_read_leaf, \
+          .hypfs.e.write = hypfs_write_custom, \
+          .hypfs.content = &contvar }
 #define boolean_runtime_only_param(_name, _var) \
     __rtparam __rtpar_##_var = \
         { .name = _name, \
           .type = OPT_BOOL, \
           .len = sizeof(_var), \
-          .par.var = &_var }
+          .par.var = &_var }; \
+    __paramfs __parfs_##_var = \
+        { .param = &__rtpar_##_var, \
+          .hypfs.e.type = XEN_HYPFS_TYPE_UINT, \
+          .hypfs.e.encoding = XEN_HYPFS_ENC_PLAIN, \
+          .hypfs.e.name = _name, \
+          .hypfs.e.size = sizeof(_var), \
+          .hypfs.e.read = hypfs_read_leaf, \
+          .hypfs.e.write = hypfs_write_bool, \
+          .hypfs.content = &_var }
 #define integer_runtime_only_param(_name, _var) \
     __rtparam __rtpar_##_var = \
         { .name = _name, \
           .type = OPT_UINT, \
           .len = sizeof(_var), \
-          .par.var = &_var }
+          .par.var = &_var }; \
+    __paramfs __parfs_##_var = \
+        { .param = &__rtpar_##_var, \
+          .hypfs.e.type = XEN_HYPFS_TYPE_UINT, \
+          .hypfs.e.encoding = XEN_HYPFS_ENC_PLAIN, \
+          .hypfs.e.name = _name, \
+          .hypfs.e.size = sizeof(_var), \
+          .hypfs.e.read = hypfs_read_leaf, \
+          .hypfs.e.write = hypfs_write_leaf, \
+          .hypfs.content = &_var }
 #define size_runtime_only_param(_name, _var) \
     __rtparam __rtpar_##_var = \
         { .name = _name, \
           .type = OPT_SIZE, \
           .len = sizeof(_var), \
-          .par.var = &_var }
+          .par.var = &_var }; \
+    __paramfs __parfs_##_var = \
+        { .param = &__rtpar_##_var, \
+          .hypfs.e.type = XEN_HYPFS_TYPE_UINT, \
+          .hypfs.e.encoding = XEN_HYPFS_ENC_PLAIN, \
+          .hypfs.e.name = _name, \
+          .hypfs.e.size = sizeof(_var), \
+          .hypfs.e.read = hypfs_read_leaf, \
+          .hypfs.e.write = hypfs_write_leaf, \
+          .hypfs.content = &_var }
 #define string_runtime_only_param(_name, _var) \
     __rtparam __rtpar_##_var = \
         { .name = _name, \
           .type = OPT_STR, \
           .len = sizeof(_var), \
-          .par.var = &_var }
+          .par.var = &_var }; \
+    __paramfs __parfs_##_var = \
+        { .param = &__rtpar_##_var, \
+          .hypfs.e.type = XEN_HYPFS_TYPE_STRING, \
+          .hypfs.e.encoding = XEN_HYPFS_ENC_PLAIN, \
+          .hypfs.e.name = _name, \
+          .hypfs.e.size = sizeof(_var), \
+          .hypfs.e.read = hypfs_read_leaf, \
+          .hypfs.e.write = hypfs_write_leaf, \
+          .hypfs.content = &_var }
 
-#define custom_runtime_param(_name, _var) \
+#define custom_runtime_param(_name, _var, contvar) \
     custom_param(_name, _var); \
-    custom_runtime_only_param(_name, _var)
+    custom_runtime_only_param(_name, _var, contvar)
 #define boolean_runtime_param(_name, _var) \
     boolean_param(_name, _var); \
     boolean_runtime_only_param(_name, _var)
@@ -123,4 +178,7 @@  extern const struct kernel_param __param_start[], __param_end[];
     string_param(_name, _var); \
     string_runtime_only_param(_name, _var)
 
+#define param_append_str(var, fmt, val) \
+    snprintf(var + strlen(var), sizeof(var) - strlen(var), fmt, val)
+
 #endif /* _XEN_PARAM_H */