[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2] xsm: add device tree labeling support



This adds support in the hypervisor and policy build toolchain for
Xen/Flask policy version 30, which adds the ability to label ARM device
tree nodes and expands the IOMEM ocontext entries to 64 bits.

Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
---
 tools/flask/policy/Makefile       |  20 ++++--
 xen/include/public/xsm/flask_op.h |   9 +++
 xen/xsm/flask/flask_op.c          |  28 ++++++++
 xen/xsm/flask/include/security.h  |  14 +++-
 xen/xsm/flask/ss/policydb.c       | 133 ++++++++++++++++++++++++++++++++------
 xen/xsm/flask/ss/policydb.h       |   7 +-
 xen/xsm/flask/ss/services.c       | 118 ++++++++++++++++++++++++++++++---
 7 files changed, 287 insertions(+), 42 deletions(-)

diff --git a/tools/flask/policy/Makefile b/tools/flask/policy/Makefile
index e564396..4be921c 100644
--- a/tools/flask/policy/Makefile
+++ b/tools/flask/policy/Makefile
@@ -20,21 +20,21 @@ MLS_CATS ?= 256
 CHECKPOLICY ?= checkpolicy
 M4 ?= m4
 
+# Output security policy version.  Leave unset to autodetect.
+OUTPUT_POLICY ?= $(BEST_POLICY_VER)
+
 ########################################
 #
 # End of configuration options
 #
 ########################################
 
-# Policy version
-# By default, checkpolicy creates the highest version policy it supports. Force
-# the use of version 24 which is the highest that Xen supports, and the first 
to
-# include the Xen policy type (needed for static device policy).
-OUTPUT_POLICY = 24
-
 POLICY_FILENAME = xenpolicy-$(shell $(MAKE) -C $(XEN_ROOT)/xen xenversion 
--no-print-directory)
 POLICY_LOADPATH = /boot
 
+# List of policy versions supported by the hypervisor
+POLICY_VER_LIST_HV = 24 30
+
 # policy source layout
 POLDIR := policy
 MODDIR := $(POLDIR)/modules
@@ -64,6 +64,14 @@ MOD_CONF := $(POLDIR)/modules.conf
 
 # checkpolicy can use the #line directives provided by -s for error reporting:
 M4PARAM := -D self_contained_policy -s
+
+# The output of checkpolicy -V is "30 (compatibility range 30-15)", and the
+# first word of the output is the maximum policy version supported.
+CHECKPOLICY_VER_MAX := $(firstword $(shell $(CHECKPOLICY) -V))
+
+# Find the highest version supported by both the hypervisor and checkpolicy
+BEST_POLICY_VER := $(shell best=24; for ver in $(POLICY_VER_LIST_HV); do if 
test $$ver -le $(CHECKPOLICY_VER_MAX); then best=$$ver; fi; done; echo $$best)
+
 CHECKPOLICY_PARAM := -t Xen -c $(OUTPUT_POLICY)
 
 # enable MLS if requested.
diff --git a/xen/include/public/xsm/flask_op.h 
b/xen/include/public/xsm/flask_op.h
index f874589..c76359c 100644
--- a/xen/include/public/xsm/flask_op.h
+++ b/xen/include/public/xsm/flask_op.h
@@ -150,6 +150,13 @@ struct xen_flask_relabel {
     uint32_t sid;
 };
 
+struct xen_flask_devicetree_label {
+    /* IN */
+    uint32_t sid;
+    uint32_t length;
+    XEN_GUEST_HANDLE(char) path;
+};
+
 struct xen_flask_op {
     uint32_t cmd;
 #define FLASK_LOAD              1
@@ -176,6 +183,7 @@ struct xen_flask_op {
 #define FLASK_DEL_OCONTEXT      22
 #define FLASK_GET_PEER_SID      23
 #define FLASK_RELABEL_DOMAIN    24
+#define FLASK_DEVICETREE_LABEL  25
     uint32_t interface_version; /* XEN_FLASK_INTERFACE_VERSION */
     union {
         struct xen_flask_load load;
@@ -195,6 +203,7 @@ struct xen_flask_op {
         struct xen_flask_ocontext ocontext;
         struct xen_flask_peersid peersid;
         struct xen_flask_relabel relabel;
+        struct xen_flask_devicetree_label devicetree_label;
     } u;
 };
 typedef struct xen_flask_op xen_flask_op_t;
diff --git a/xen/xsm/flask/flask_op.c b/xen/xsm/flask/flask_op.c
index 84c7fec..47aacc1 100644
--- a/xen/xsm/flask/flask_op.c
+++ b/xen/xsm/flask/flask_op.c
@@ -563,6 +563,27 @@ static int flask_security_load(struct xen_flask_load *load)
     return ret;
 }
 
+static int flask_devicetree_label(struct xen_flask_devicetree_label *arg)
+{
+    int rv;
+    char *buf;
+    u32 sid = arg->sid;
+    u32 perm = sid ? SECURITY__ADD_OCONTEXT : SECURITY__DEL_OCONTEXT;
+
+    rv = domain_has_security(current->domain, perm);
+    if ( rv )
+        return rv;
+
+    rv = flask_copyin_string(arg->path, &buf, arg->length, PAGE_SIZE);
+    if ( rv )
+        return rv;
+
+    /* buf is consumed or freed by this function */
+    rv = security_devicetree_setlabel(buf, sid);
+
+    return rv;
+}
+
 #ifndef COMPAT
 
 static int flask_ocontext_del(struct xen_flask_ocontext *arg)
@@ -790,6 +811,10 @@ ret_t do_flask_op(XEN_GUEST_HANDLE_PARAM(xsm_op_t) 
u_flask_op)
         rv = flask_relabel_domain(&op.u.relabel);
         break;
 
+    case FLASK_DEVICETREE_LABEL:
+        rv = flask_devicetree_label(&op.u.devicetree_label);
+        break;
+
     default:
         rv = -ENOSYS;
     }
@@ -848,6 +873,9 @@ CHECK_flask_transition;
 #define flask_security_get_bool compat_security_get_bool
 #define flask_security_set_bool compat_security_set_bool
 
+#define xen_flask_devicetree_label compat_flask_devicetree_label
+#define flask_devicetree_label compat_devicetree_label
+
 #define xen_flask_op_t compat_flask_op_t
 #undef ret_t
 #define ret_t int
diff --git a/xen/xsm/flask/include/security.h b/xen/xsm/flask/include/security.h
index d07bae0..34bbe62 100644
--- a/xen/xsm/flask/include/security.h
+++ b/xen/xsm/flask/include/security.h
@@ -30,10 +30,16 @@
 #define POLICYDB_VERSION_POLCAP                22
 #define POLICYDB_VERSION_PERMISSIVE    23
 #define POLICYDB_VERSION_BOUNDARY      24
+#define POLICYDB_VERSION_FILENAME_TRANS        25
+#define POLICYDB_VERSION_ROLETRANS     26
+#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS   27
+#define POLICYDB_VERSION_DEFAULT_TYPE  28
+#define POLICYDB_VERSION_CONSTRAINT_NAMES      29
+#define POLICYDB_VERSION_XEN_DEVICETREE 30
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_BOUNDARY
+#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_XEN_DEVICETREE
 
 enum flask_bootparam_t {
     FLASK_BOOTPARAM_PERMISSIVE,
@@ -82,6 +88,8 @@ int security_ioport_sid(u32 ioport, u32 *out_sid);
 
 int security_device_sid(u32 device, u32 *out_sid);
 
+int security_devicetree_sid(const char *path, u32 *out_sid);
+
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
                                                                     u16 
tclass);
 
@@ -96,5 +104,7 @@ int security_iterate_ioport_sids(u32 start, u32 end,
 int security_ocontext_add(u32 ocontext, unsigned long low,
                            unsigned long high, u32 sid);
 
-int security_ocontext_del(u32 ocontext, unsigned int low, unsigned int high);
+int security_ocontext_del(u32 ocontext, unsigned long low, unsigned long high);
+
+int security_devicetree_setlabel(char *path, u32 sid);
 #endif /* _FLASK_SECURITY_H_ */
diff --git a/xen/xsm/flask/ss/policydb.c b/xen/xsm/flask/ss/policydb.c
index b88ea56..a1060b1 100644
--- a/xen/xsm/flask/ss/policydb.c
+++ b/xen/xsm/flask/ss/policydb.c
@@ -74,55 +74,55 @@ static struct policydb_compat_info policydb_compat[] = {
     {
         .version        = POLICYDB_VERSION_BASE,
         .sym_num        = SYM_NUM - 3,
-        .ocon_num       = OCON_NUM - 1,
+        .ocon_num       = 4,
         .target_type    = TARGET_XEN_OLD,
     },
     {
         .version        = POLICYDB_VERSION_BOOL,
         .sym_num        = SYM_NUM - 2,
-        .ocon_num       = OCON_NUM - 1,
+        .ocon_num       = 4,
         .target_type    = TARGET_XEN_OLD,
     },
     {
         .version        = POLICYDB_VERSION_IPV6,
         .sym_num        = SYM_NUM - 2,
-        .ocon_num       = OCON_NUM,
+        .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
         .version        = POLICYDB_VERSION_NLCLASS,
         .sym_num        = SYM_NUM - 2,
-        .ocon_num       = OCON_NUM,
+        .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
         .version        = POLICYDB_VERSION_MLS,
         .sym_num        = SYM_NUM,
-        .ocon_num       = OCON_NUM,
+        .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
         .version        = POLICYDB_VERSION_AVTAB,
         .sym_num        = SYM_NUM,
-        .ocon_num       = OCON_NUM,
+        .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
        .version        = POLICYDB_VERSION_RANGETRANS,
        .sym_num        = SYM_NUM,
-       .ocon_num       = OCON_NUM,
+       .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
        .version        = POLICYDB_VERSION_POLCAP,
        .sym_num        = SYM_NUM,
-       .ocon_num       = OCON_NUM,
+       .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
        .version        = POLICYDB_VERSION_PERMISSIVE,
        .sym_num        = SYM_NUM,
-       .ocon_num       = OCON_NUM,
+       .ocon_num       = 5,
         .target_type    = TARGET_XEN_OLD,
     },
     {
@@ -134,7 +134,13 @@ static struct policydb_compat_info policydb_compat[] = {
     {
        .version        = POLICYDB_VERSION_BOUNDARY,
        .sym_num        = SYM_NUM,
-       .ocon_num       = OCON_NUM,
+       .ocon_num       = OCON_DEVICE + 1,
+        .target_type    = TARGET_XEN,
+    },
+    {
+       .version        = POLICYDB_VERSION_XEN_DEVICETREE,
+       .sym_num        = SYM_NUM,
+       .ocon_num       = OCON_DTREE + 1,
         .target_type    = TARGET_XEN,
     },
 };
@@ -634,7 +640,7 @@ static void ocontext_destroy(struct ocontext *c, int i)
 {
     context_destroy(&c->context[0]);
     context_destroy(&c->context[1]);
-    if ( i == OCON_ISID )
+    if ( i == OCON_ISID || i == OCON_DTREE )
         xfree(c->u.name);
     xfree(c);
 }
@@ -1040,8 +1046,8 @@ bad:
     goto out;
 }
 
-static int read_cons_helper(struct constraint_node **nodep, int ncons,
-                                                    int allowxtarget, void *fp)
+static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
+                            int ncons, int allowxtarget, void *fp)
 {
     struct constraint_node *c, *lc;
     struct constraint_expr *e, *le;
@@ -1115,6 +1121,23 @@ static int read_cons_helper(struct constraint_node 
**nodep, int ncons,
                     depth++;
                     if ( ebitmap_read(&e->names, fp) )
                         return -EINVAL;
+                    if ( p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES )
+                    {
+                        struct ebitmap dummy;
+                        ebitmap_init(&dummy);
+                        if ( ebitmap_read(&dummy, fp) )
+                            return -EINVAL;
+                        ebitmap_destroy(&dummy);
+
+                        ebitmap_init(&dummy);
+                        if ( ebitmap_read(&dummy, fp) )
+                            return -EINVAL;
+                        ebitmap_destroy(&dummy);
+
+                        rc = next_entry(buf, fp, sizeof(u32));
+                        if ( rc < 0 )
+                            return rc;
+                    }
                 break;
                 default:
                     return -EINVAL;
@@ -1184,7 +1207,7 @@ static int class_read(struct policydb *p, struct hashtab 
*h, void *fp)
             goto bad;
     }
 
-    rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp);
+    rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
     if ( rc )
         goto bad;
 
@@ -1195,11 +1218,27 @@ static int class_read(struct policydb *p, struct 
hashtab *h, void *fp)
         if ( rc < 0 )
             goto bad;
         ncons = le32_to_cpu(buf[0]);
-        rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp);
+        rc = read_cons_helper(p, &cladatum->validatetrans, ncons, 1, fp);
         if ( rc )
             goto bad;
     }
 
+    if ( p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS )
+    {
+        rc = next_entry(buf, fp, sizeof(u32) * 3);
+        if ( rc )
+            goto bad;
+        /* these values are ignored by Xen */
+    }
+
+    if ( p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE )
+    {
+        rc = next_entry(buf, fp, sizeof(u32) * 1);
+        if ( rc )
+            goto bad;
+        /* ignored by Xen */
+    }
+
     rc = hashtab_insert(h, key, cladatum);
     if ( rc )
         goto bad;
@@ -1874,7 +1913,10 @@ int policydb_read(struct policydb *p, void *fp)
             ltr->next = tr;
         else
             p->role_tr = tr;
-        rc = next_entry(buf, fp, sizeof(u32)*3);
+        if ( p->policyvers >= POLICYDB_VERSION_ROLETRANS )
+            rc = next_entry(buf, fp, sizeof(u32)*4);
+        else
+            rc = next_entry(buf, fp, sizeof(u32)*3);
         if ( rc < 0 )
             goto bad;
         tr->role = le32_to_cpu(buf[0]);
@@ -1921,6 +1963,20 @@ int policydb_read(struct policydb *p, void *fp)
         lra = ra;
     }
 
+    if ( p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS )
+    {
+        rc = next_entry(buf, fp, sizeof(u32));
+        if ( rc )
+            goto bad;
+        nel = le32_to_cpu(buf[0]);
+        if ( nel )
+        {
+            printk(KERN_ERR "Flask:  unsupported genfs config data\n");
+            rc = -EINVAL;
+            goto bad;
+        }
+    }
+
     rc = policydb_index_classes(p);
     if ( rc )
         goto bad;
@@ -1999,11 +2055,23 @@ int policydb_read(struct policydb *p, void *fp)
                         "Old xen policy does not support iomemcon");
                     goto bad;
                 }
-                rc = next_entry(buf, fp, sizeof(u32) *2);
-                if ( rc < 0 )
-                    goto bad;
-                c->u.iomem.low_iomem = le32_to_cpu(buf[0]);
-                c->u.iomem.high_iomem = le32_to_cpu(buf[1]);
+                if ( p->policyvers >= POLICYDB_VERSION_XEN_DEVICETREE )
+                {
+                    u64 b64[2];
+                    rc = next_entry(b64, fp, sizeof(u64) *2);
+                    if ( rc < 0 )
+                        goto bad;
+                    c->u.iomem.low_iomem = le64_to_cpu(b64[0]);
+                    c->u.iomem.high_iomem = le64_to_cpu(b64[1]);
+                }
+                else
+                {
+                    rc = next_entry(buf, fp, sizeof(u32) *2);
+                    if ( rc < 0 )
+                        goto bad;
+                    c->u.iomem.low_iomem = le32_to_cpu(buf[0]);
+                    c->u.iomem.high_iomem = le32_to_cpu(buf[1]);
+                }
                 rc = context_read_and_validate(&c->context[0], p, fp);
                 if ( rc )
                     goto bad;
@@ -2023,6 +2091,29 @@ int policydb_read(struct policydb *p, void *fp)
                 if ( rc )
                     goto bad;
                 break;
+            case OCON_DTREE:
+                if ( p->target_type != TARGET_XEN )
+                {
+                    printk(KERN_ERR
+                        "Old xen policy does not support devicetreecon");
+                    goto bad;
+                }
+                rc = next_entry(buf, fp, sizeof(u32));
+                if ( rc < 0 )
+                    goto bad;
+                len = le32_to_cpu(buf[0]);
+                rc = -ENOMEM;
+                c->u.name = xmalloc_array(char, len + 1);
+                if (!c->u.name)
+                    goto bad;
+                rc = next_entry(c->u.name, fp, len);
+                if ( rc < 0 )
+                    goto bad;
+                c->u.name[len] = 0;
+                rc = context_read_and_validate(&c->context[0], p, fp);
+                if ( rc )
+                    goto bad;
+                break;
             default:
                 printk(KERN_ERR
                        "Flask:  unsupported object context config data\n");
diff --git a/xen/xsm/flask/ss/policydb.h b/xen/xsm/flask/ss/policydb.h
index b176300..30be71a 100644
--- a/xen/xsm/flask/ss/policydb.h
+++ b/xen/xsm/flask/ss/policydb.h
@@ -154,8 +154,8 @@ struct ocontext {
                 u32 high_ioport;
         } ioport;
         struct {
-                u32 low_iomem;
-                u32 high_iomem;
+                u64 low_iomem;
+                u64 high_iomem;
         } iomem;
     } u;
     struct context context[2];    /* security context(s) */
@@ -180,7 +180,8 @@ struct ocontext {
 #define OCON_IOPORT  2    /* io ports */
 #define OCON_IOMEM   3    /* io memory */
 #define OCON_DEVICE  4    /* pci devices */
-#define OCON_NUM     5
+#define OCON_DTREE   5    /* device tree nodes */
+#define OCON_NUM     6
 #define OCON_NUM_OLD 7
 
 /* The policy database */
diff --git a/xen/xsm/flask/ss/services.c b/xen/xsm/flask/ss/services.c
index f0e459a..f31d7d7 100644
--- a/xen/xsm/flask/ss/services.c
+++ b/xen/xsm/flask/ss/services.c
@@ -1831,6 +1831,41 @@ out:
     return rc;
 }
 
+int security_devicetree_sid(const char *path, u32 *out_sid)
+{
+    struct ocontext *c;
+    int rc = 0;
+
+    POLICY_RDLOCK;
+
+    c = policydb.ocontexts[OCON_DTREE];
+    while ( c )
+    {
+        if ( strcmp(c->u.name, path) == 0 )
+            break;
+        c = c->next;
+    }
+
+    if ( c )
+    {
+        if ( !c->sid[0] )
+        {
+            rc = sidtab_context_to_sid(&sidtab, &c->context[0], &c->sid[0]);
+            if ( rc )
+                goto out;
+        }
+        *out_sid = c->sid[0];
+    }
+    else
+    {
+        *out_sid = SECINITSID_DEVICE;
+    }
+
+out:
+    POLICY_RDUNLOCK;
+    return rc;
+}
+
 int security_find_bool(const char *name)
 {
     int i, rv = -ENOENT;
@@ -2131,9 +2166,8 @@ int security_ocontext_add( u32 ocon, unsigned long low, 
unsigned long high
                 c->u.iomem.high_iomem == high && c->sid[0] == sid)
                 break;
 
-            printk("%s: IO Memory overlap with entry %#x - %#x\n",
-                   __FUNCTION__, c->u.iomem.low_iomem,
-                   c->u.iomem.high_iomem);
+            printk("%s: IO Memory overlap with entry %#"PRIx64" - 
%#"PRIx64"\n",
+                   __FUNCTION__, c->u.iomem.low_iomem, c->u.iomem.high_iomem);
             ret = -EEXIST;
             break;
         }
@@ -2188,7 +2222,7 @@ int security_ocontext_add( u32 ocon, unsigned long low, 
unsigned long high
     return ret;
 }
 
-int security_ocontext_del( u32 ocon, unsigned int low, unsigned int high )
+int security_ocontext_del( u32 ocon, unsigned long low, unsigned long high )
 {
     int ret = 0;
     struct ocontext *c, *before_c;
@@ -2217,7 +2251,7 @@ int security_ocontext_del( u32 ocon, unsigned int low, 
unsigned int high )
             }
         }
 
-        printk("%s: ocontext not found: pirq %d\n", __FUNCTION__, low);
+        printk("%s: ocontext not found: pirq %ld\n", __FUNCTION__, low);
         ret = -ENOENT;
         break;
 
@@ -2243,8 +2277,8 @@ int security_ocontext_del( u32 ocon, unsigned int low, 
unsigned int high )
             }
         }
 
-        printk("%s: ocontext not found: ioport %#x - %#x\n", __FUNCTION__,
-                low, high);
+        printk("%s: ocontext not found: ioport %#lx - %#lx\n",
+                __FUNCTION__, low, high);
         ret = -ENOENT;
         break;
 
@@ -2270,8 +2304,8 @@ int security_ocontext_del( u32 ocon, unsigned int low, 
unsigned int high )
             }
         }
 
-        printk("%s: ocontext not found: iomem %#x - %#x\n", __FUNCTION__,
-                low, high);
+        printk("%s: ocontext not found: iomem %#lx - %#lx\n",
+                __FUNCTION__, low, high);
         ret = -ENOENT;
         break;
 
@@ -2296,7 +2330,7 @@ int security_ocontext_del( u32 ocon, unsigned int low, 
unsigned int high )
             }
         }
 
-        printk("%s: ocontext not found: pcidevice %#x\n", __FUNCTION__, low);
+        printk("%s: ocontext not found: pcidevice %#lx\n", __FUNCTION__, low);
         ret = -ENOENT;
         break;
 
@@ -2308,3 +2342,67 @@ int security_ocontext_del( u32 ocon, unsigned int low, 
unsigned int high )
     POLICY_WRUNLOCK;
     return ret;
 }
+
+int security_devicetree_setlabel(char *path, u32 sid)
+{
+    int ret = 0;
+    struct ocontext *c;
+    struct ocontext **pcurr;
+    struct ocontext *add = NULL;
+
+    if ( sid )
+    {
+        add = xzalloc(struct ocontext);
+        if ( add == NULL )
+        {
+            xfree(path);
+            return -ENOMEM;
+        }
+        add->sid[0] = sid;
+        add->u.name = path;
+    }
+    else
+    {
+        ret = -ENOENT;
+    }
+
+    POLICY_WRLOCK;
+
+    pcurr = &policydb.ocontexts[OCON_DTREE];
+    c = *pcurr;
+    while ( c )
+    {
+        if ( strcmp(c->u.name, path) == 0 )
+        {
+            if ( sid )
+            {
+                ret = -EEXIST;
+                break;
+            }
+            else
+            {
+                *pcurr = c->next;
+                xfree(c->u.name);
+                xfree(c);
+                ret = 0;
+                break;
+            }
+        }
+        pcurr = &c->next;
+        c = *pcurr;
+    }
+
+    if ( add && ret == 0 )
+    {
+        add->next = policydb.ocontexts[OCON_DTREE];
+        policydb.ocontexts[OCON_DTREE] = add;
+        add = NULL;
+        path = NULL;
+    }
+
+    POLICY_WRUNLOCK;
+
+    xfree(add);
+    xfree(path);
+    return ret;
+}
-- 
2.1.0


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.