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

Re: [Minios-devel] [UNIKRAFT PATCH v2 1/7] lib/uklibparam: Introduce the library parameter



Hi Sharan and Felipe,

I have a suggestion, please see inline.

On 8/15/19 1:48 PM, Felipe Huici wrote:
> Hi Sharan,
> 
> Thanks for fixing the extensive list of comments from Florian. There are 
> still cases where providing maliciously-formatted strings (e.g., 
> test.testarr2=\"'''''''''''''''''''\"\"\"wef238@#*(@#\") causes unexpected 
> behavior, but I'm satisfied that the code can handle most common typos/errors 
> -- we're not trying to be robust to intentionally malicious behavior.
> 
> I have one more minor comment inline.
> 
> -- Felipe
> 
> On 13.08.19, 14:37, "Minios-devel on behalf of Sharan Santhanam" 
> <minios-devel-bounces@xxxxxxxxxxxxxxxxxxxx on behalf of 
> Sharan.Santhanam@xxxxxxxxx> wrote:
> 
>     This patch provides the header necessary to register a variable as an
>     boot argument with Unikraft that may depend on user input. The patch
>     provides an implementation for parsing scalar arguments.
>     
>     Change since v1:
>     
>     Signed-off-by: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
>     ---
>      MAINTAINERS.md                       |   5 +
>      lib/Config.uk                        |   1 +
>      lib/Makefile.uk                      |   1 +
>      lib/ukboot/Makefile.uk               |   1 +
>      lib/ukboot/exportsyms.uk             |   1 +
>      lib/ukboot/include/uk/version.h      |   6 +
>      lib/ukboot/version.c                 |  11 +
>      lib/uklibparam/Config.uk             |   5 +
>      lib/uklibparam/Makefile.uk           |   7 +
>      lib/uklibparam/exportsyms.uk         |   2 +
>      lib/uklibparam/include/uk/libparam.h | 414 +++++++++++++++++++++
>      lib/uklibparam/param.c               | 531 +++++++++++++++++++++++++++
>      12 files changed, 985 insertions(+)
>      create mode 100644 lib/ukboot/include/uk/version.h
>      create mode 100644 lib/ukboot/version.c
>      create mode 100644 lib/uklibparam/Config.uk
>      create mode 100644 lib/uklibparam/Makefile.uk
>      create mode 100644 lib/uklibparam/exportsyms.uk
>      create mode 100644 lib/uklibparam/include/uk/libparam.h
>      create mode 100644 lib/uklibparam/param.c
>     
>     diff --git a/MAINTAINERS.md b/MAINTAINERS.md
>     index e3eb898d..690ca7eb 100644
>     --- a/MAINTAINERS.md
>     +++ b/MAINTAINERS.md
>     @@ -126,6 +126,11 @@ Try to look for the most precise areas first. In 
> case nothing fits use
>       L:      minios-devel@xxxxxxxxxxxxx
>       F: lib/ukdebug/*
>      
>     + LIBUKLIBPARAM
>     + M: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
>     + L:      minios-devel@xxxxxxxxxxxxx
>     + F: lib/uklibparam/*
>     +
>       UNIKRAFT GENERAL
>       M:      Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
>       M:      Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
>     diff --git a/lib/Config.uk b/lib/Config.uk
>     index f61d3b1e..e7b26c8e 100644
>     --- a/lib/Config.uk
>     +++ b/lib/Config.uk
>     @@ -49,3 +49,4 @@ source "lib/ukswrand/Config.uk"
>      source "lib/ukbus/Config.uk"
>      source "lib/uksglist/Config.uk"
>      source "lib/uknetdev/Config.uk"
>     +source "lib/uklibparam/Config.uk"
>     diff --git a/lib/Makefile.uk b/lib/Makefile.uk
>     index b7ad6287..6f817afc 100644
>     --- a/lib/Makefile.uk
>     +++ b/lib/Makefile.uk
>     @@ -26,3 +26,4 @@ $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/ukmpi))
>      $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/ukbus))
>      $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/uksglist))
>      $(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/uknetdev))
>     +$(eval $(call _import_lib,$(CONFIG_UK_BASE)/lib/uklibparam))
>     diff --git a/lib/ukboot/Makefile.uk b/lib/ukboot/Makefile.uk
>     index 55f205df..ea052019 100644
>     --- a/lib/ukboot/Makefile.uk
>     +++ b/lib/ukboot/Makefile.uk
>     @@ -4,6 +4,7 @@ CINCLUDES-$(CONFIG_LIBUKBOOT)             += 
> -I$(LIBUKBOOT_BASE)/include
>      CXXINCLUDES-$(CONFIG_LIBUKBOOT)  += -I$(LIBUKBOOT_BASE)/include
>      
>      LIBUKBOOT_SRCS-y += $(LIBUKBOOT_BASE)/boot.c
>     +LIBUKBOOT_SRCS-y += $(LIBUKBOOT_BASE)/version.c

The version changes in ukboot library should be in a patch with only
those changes and the current patch should only contain uklibparam changes.

>      
>      # The main() is in the separate library to fool the LTO. Which is
>      # trying to resolve the main() function call to whatever is available
>     diff --git a/lib/ukboot/exportsyms.uk b/lib/ukboot/exportsyms.uk
>     index 3edc6c6a..4bce9274 100644
>     --- a/lib/ukboot/exportsyms.uk
>     +++ b/lib/ukboot/exportsyms.uk
>     @@ -1,3 +1,4 @@
>      ukplat_entry_argp
>      ukplat_entry
>      main
>     +uk_version

This should go in the ukboot version patch.

>     diff --git a/lib/ukboot/include/uk/version.h 
> b/lib/ukboot/include/uk/version.h
>     new file mode 100644
>     index 00000000..a1b31c3d
>     --- /dev/null
>     +++ b/lib/ukboot/include/uk/version.h
>     @@ -0,0 +1,6 @@
>     +#ifndef _UK_VERSION_H
>     +#define _UK_VERSION_H
>     +
>     +void uk_version(void);
>     +
>     +#endif /* _UK_VERSION_H */

This should go in the ukboot version patch.

>     diff --git a/lib/ukboot/version.c b/lib/ukboot/version.c
>     new file mode 100644
>     index 00000000..559e842d
>     --- /dev/null
>     +++ b/lib/ukboot/version.c
>     @@ -0,0 +1,11 @@
>     +#include <uk/version.h>
>     +#include <uk/essentials.h>
>     +#include <stdio.h>
>     +
>     +void uk_version(void)
>     +{
>     + printf("Unikraft "
>     +         STRINGIFY(UK_CODENAME) " "
>     +         STRINGIFY(UK_FULLVERSION) "\n");
>     +}
>     +

This should go in the ukboot version patch.

>     diff --git a/lib/uklibparam/Config.uk b/lib/uklibparam/Config.uk
>     new file mode 100644
>     index 00000000..18bb43d6
>     --- /dev/null
>     +++ b/lib/uklibparam/Config.uk
>     @@ -0,0 +1,5 @@
>     +config LIBUKLIBPARAM
>     +       bool "uk library parameter: Pass arguments to a unikraft library"
>     +       default n
>     +       select LIBUKDEBUG
>     +       select LIBNOLIBC if !HAVE_LIBC
>     diff --git a/lib/uklibparam/Makefile.uk b/lib/uklibparam/Makefile.uk
>     new file mode 100644
>     index 00000000..3d450b86
>     --- /dev/null
>     +++ b/lib/uklibparam/Makefile.uk
>     @@ -0,0 +1,7 @@
>     +$(eval $(call addlib_s,libuklibparam,$(CONFIG_LIBUKLIBPARAM)))
>     +
>     +ASINCLUDES-y     += -I$(LIBUKLIBPARAM_BASE)/include
>     +CINCLUDES-y      += -I$(LIBUKLIBPARAM_BASE)/include
>     +CXXINCLUDES-y    += -I$(LIBUKLIBPARAM_BASE)/include
>     +
>     +LIBUKLIBPARAM_SRCS-y += $(LIBUKLIBPARAM_BASE)/param.c
>     diff --git a/lib/uklibparam/exportsyms.uk b/lib/uklibparam/exportsyms.uk
>     new file mode 100644
>     index 00000000..94b6ca77
>     --- /dev/null
>     +++ b/lib/uklibparam/exportsyms.uk
>     @@ -0,0 +1,2 @@
>     +uk_libparam_parse
>     +_uk_libparam_lib_add
>     diff --git a/lib/uklibparam/include/uk/libparam.h 
> b/lib/uklibparam/include/uk/libparam.h
>     new file mode 100644
>     index 00000000..454cc0d6
>     --- /dev/null
>     +++ b/lib/uklibparam/include/uk/libparam.h
>     @@ -0,0 +1,414 @@
>     +/* SPDX-License-Identifier: BSD-3-Clause */
>     +/*
>     + * Authors: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
>     + *
>     + * Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights 
> reserved.
>     + *
>     + * Redistribution and use in source and binary forms, with or without
>     + * modification, are permitted provided that the following conditions
>     + * are met:
>     + *
>     + * 1. Redistributions of source code must retain the above copyright
>     + *    notice, this list of conditions and the following disclaimer.
>     + * 2. Redistributions in binary form must reproduce the above copyright
>     + *    notice, this list of conditions and the following disclaimer in the
>     + *    documentation and/or other materials provided with the 
> distribution.
>     + * 3. Neither the name of the copyright holder nor the names of its
>     + *    contributors may be used to endorse or promote products derived 
> from
>     + *    this software without specific prior written permission.
>     + *
>     + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
> "AS IS"
>     + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
> THE
>     + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> PURPOSE
>     + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
> CONTRIBUTORS BE
>     + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
>     + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
>     + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
> BUSINESS
>     + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
> IN
>     + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
> OTHERWISE)
>     + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
> OF THE
>     + * POSSIBILITY OF SUCH DAMAGE.
>     + *
>     + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
>     + */
>     +#ifndef __UK_LIBPARAM_H
>     +#define __UK_LIBPARAM_H
>     +
>     +#include <uk/config.h>
>     +#ifndef __ASSEMBLY__
>     +#include <uk/ctors.h>
>     +#include <uk/arch/types.h>
>     +#include <uk/essentials.h>
>     +#include <uk/list.h>
>     +#include <uk/print.h>
>     +
>     +#ifdef __cplusplus
>     +extern C {
>     +#endif /* __cplusplus */
>     +#endif /* !__ASSEMBLY__ */
>     +
>     +/**
>     + * Variable name prefix/suffix
>     + */
>     +#define UK_LIBPARAM_SECTION      uk_lib_arg
>     +/**
>     + * Library: section suffix for the name and the
>     + * parameter.
>     + */
>     +#define LIB_PARAM_SUFFIX __lib_param
>     +#define LIB_NAME_SUFFIX          __lib_str
>     +/**
>     + * Library variable names for the name and the
>     + * parameter.
>     + */
>     +#define LIB_PARAMVAR_PREFIX      _lib_param_
>     +#define LIB_NAMEVAR_PREFIX       _lib_name_
>     +/**
>     + * Parameter within a library: section suffix for the name and the
>     + * parameter.
>     + */
>     +#define PARAM_SECTION_SUFFIX     __param_arg
>     +#define PARAM_NAME_SUFFIX        __param_str
>     +/**
>     + * Parameter within a library: variable name prefix for the name and the
>     + * parameter.
>     + */
>     +#define PARAM_PARAMVAR_PREFIX    _param_param_
>     +#define PARAM_NAMEVAR_PREFIX     _param_name_
>     +
>     +#define __STRINGCONCAT(x, y)     x ## y
>     +
>     +/**
>     + * Create a section name.
>     + * @param libname
>     + *       The library name
>     + * @param section
>     + *       The section suffix for the library
>     + */
>     +#define _LIB_PARAM_SECTION_NAME(libname, section_name)           \
>     +                         __STRINGCONCAT(libname, section_name)
>     +
>     +/**
>     + * Macros to denote the start / stop of a section.
>     + */
>     +#define _SECTION_START(name)     __STRINGCONCAT(__start_, name)
>     +#define _SECTION_STOP(name)      __STRINGCONCAT(__stop_, name)
>     +
>     +/**
>     + * Make sure there is a dummy implementation for the UK_PARAM family of
>     + * functions.
>     + */
>     +#ifndef CONFIG_LIBUKLIBPARAM
>     +/**
>     + * Declare a library param.
>     + * @param name
>     + *       The name of the library param.
>     + * @param type
>     + *       The type of the param.
>     + */
>     +#define UK_LIB_PARAM(name, type)
>     +
>     +#else /* !CONFIG_LIBUKLIBPARAM */
>     +/**
>     + * Each parameter is bit-mapped as follows:
>     + * ---------------------------------------
>     + * | sign | copy | size of the parameter |
>     + * ---------------------------------------
>     + * 7     6      5                       0
>     + */
>     +/**
>     + * Sign bit: Shift & Mask
>     + */
>     +#define PARAM_SIGN_SHIFT (7)
>     +#define PARAM_SIGN_MASK          (0x1)
>     +/**
>     + * Shallow copy: Shift & Mask
>     + */
>     +#define PARAM_SCOPY_SHIFT        (6)
>     +#define PARAM_SCOPY_MASK (0x1)
>     +/**
>     + * Size of the param: Shift & Mask
>     + */
>     +#define PARAM_SIZE_SHIFT (0x0)
>     +#define PARAM_SIZE_MASK         (0x3F)
>     +
>     +#ifndef __ASSEMBLY__
>     +/**
>     + * Get the parameter type.
>     + * @param sign
>     + *       The sign of the data type.
>     + * @param scopy
>     + *       Flag to indicate shallow copy.
>     + *       1 - shallow copy.
>     + *       0 - data copy.
>     + * @param size
>     + *       The size of the parameter.
>     + */
>     +#define PARAM_TYPE(sign, scopy, size)                            \
>     +         (                                               \
>     +                 ((((__u8) (sign & PARAM_SIGN_MASK)) <<  \
>     +                           PARAM_SIGN_SHIFT) |           \
>     +                 (((__u8) (scopy & PARAM_SCOPY_MASK)) << \
>     +                           PARAM_SCOPY_SHIFT) |          \
>     +                 (((__u8) (size & PARAM_SIZE_MASK)) <<   \
>     +                           PARAM_SIZE_SHIFT))            \
>     +         )
>     +
>     +/**
>     + * Support data types as parameters
>     + */
>     +#define _LIB_PARAM___s8          PARAM_TYPE(1, 0, sizeof(__s8))
>     +#define _LIB_PARAM_char          _LIB_PARAM___s8
>     +#define _LIB_PARAM___u8          PARAM_TYPE(0, 0, sizeof(__u8))
>     +#define _LIB_PARAM___s16 PARAM_TYPE(1, 0, sizeof(__s16))
>     +#define _LIB_PARAM___u16 PARAM_TYPE(0, 0, sizeof(__u16))
>     +#define _LIB_PARAM___s32 PARAM_TYPE(1, 0, sizeof(__s32))
>     +#define _LIB_PARAM_int           _LIB_PARAM___s32
>     +#define _LIB_PARAM___u32 PARAM_TYPE(0, 0, sizeof(__u32))
>     +#define _LIB_PARAM___s64 PARAM_TYPE(1, 0, sizeof(__s64))
>     +#define _LIB_PARAM___u64 PARAM_TYPE(0, 0, sizeof(__u64))
>     +
>     +struct uk_param {
>     + /* The name of the param */
>     + const char *name;
>     + /* Type information for the param */
>     + const __u8 param_type;
>     + /* Type information for the variable size param */
>     + const __u8 param_size;
>     + /* Define a reference to location of the parameter */
>     + __uptr addr;
>     +};
>     +
>     +struct uk_lib_section {
>     + /* Library name */
>     + const char *lib_name;
>     + /* Section header of the uk_param args */
>     + struct uk_param *sec_addr_start;
>     + /* Length of the section */
>     + __u32   len;
>     + /* Next section entry */
>     + struct uk_list_head next;
>     +};
>     +
>     +/**
>     + * Parse through the kernel parameter
>     + * @param progname
>     + *       The application name
>     + * @param argc
>     + *       The number of arguments
>     + * @param argv
>     + *       Reference to the command line arguments
>     + * @return
>     + *       On success, return the number of argument parsed.
>     + *       On Failure, return the error code.
>     + */
>     +int uk_libparam_parse(const char *progname, int argc, char **argv);
>     +
>     +/**
>     + * Register the library containing kernel parameter.
>     + *
>     + * @param lib_sec
>     + *       A reference to the uk_lib_section.
>     + */
>     +void _uk_libparam_lib_add(struct uk_lib_section *lib_sec);
>     +
>     +/**
>     + * Add a variable to a specific section.
>     + * @param section_name
>     + *       The name of the section.
>     + * @param align_type
>     + *       The alignment requirements for the variable definitions.
>     + */
>     +#define _LIB_PARAM_SECTION_ADD(section_name, align_type)         \
>     +                         __attribute__ ((used,                   \
>     +                                         section(                \
>     +                                 __STRINGIFY(section_name)),     \
>     +                                 aligned(sizeof(align_type))     \
>     +                                      ))
>     +/**
>     + * Create a constructor name.
>     + * @param libname
>     + *       The library name.
>     + * @param suffix
>     + *       The suffix appended to the library name.
>     + */
>     +#define _LIB_UK_CONSTRUCT_NAME(libname, suffix)                  \
>     +        __STRINGCONCAT(libname, suffix)
>     +
>     +/**
>     + * Create a variable name
>     + * @param prefix
>     + *       The prefix to the variable name.
>     + * @param name
>     + *       The name of the variable
>     + */
>     +#define _LIB_VARNAME_SET(prefix, name)                           \
>     +                  __STRINGCONCAT(prefix, name)
>     +
>     +/**
>     + * Import the section header.
>     + * @param libname
>     + *       The library name.
>     + * @param section_suffix
>     + *       The suffix string for the section name
>     + */
>     +#define UK_LIB_IMPORT_SECTION_PARAMS(libname, section_suffix)            
> \
>     + extern char *_SECTION_START(                                    \
>     +                 _LIB_PARAM_SECTION_NAME(libname,                \
>     +                                         section_suffix));       \
>     + extern char *_SECTION_STOP(                                     \
>     +                 _LIB_PARAM_SECTION_NAME(libname,                \
>     +                                         section_suffix))        \
>     +
>     +/**
>     + * Create a library name variable and uk_lib_section for each library.
>     + * @param libname
>     + *       The library name.
>     + */
>     +#define UK_LIB_SECTION_CREATE(section, libname)                          
> \
>     + static const char                                               \
>     +         _LIB_VARNAME_SET(LIB_NAMEVAR_PREFIX, libname)[] =       \
>     +                                         __STRINGIFY(libname);   \
>     + static _LIB_PARAM_SECTION_ADD(                                  \
>     +                               _LIB_PARAM_SECTION_NAME(section,  \
>     +                                         LIB_PARAM_SUFFIX),      \
>     +                                         void *)                 \
>     +         struct uk_lib_section                                   \
>     +                 _LIB_VARNAME_SET(LIB_PARAMVAR_PREFIX, libname) = \
>     +                 { .lib_name = __NULL,                           \
>     +                   .sec_addr_start = __NULL, .len = 0            \
>     +                 }
>     +
>     +#define UK_LIB_CTOR_PRIO 1
>     +
>     +#define UK_LIB_CONSTRUCTOR_SETUP(prio, name)                             
> \
>     + __UK_CTOR_FUNC(prio, name)
>     +
>     +/**
>     + * Create a constructor to initialize the parameters in the library.
>     + */
>     +#define UK_LIB_CONSTRUCTOR_CREATE(libname)                               
> \
>     + static void _LIB_UK_CONSTRUCT_NAME(libname, process_arg)(void)  \
>     + {                                                               \
>     +         int len = (__uptr) &_SECTION_STOP(                      \
>     +                         _LIB_PARAM_SECTION_NAME(                \
>     +                                 libname, PARAM_SECTION_SUFFIX)  \
>     +                                 ) -                             \
>     +                   (__uptr) &_SECTION_START(                     \
>     +                         _LIB_PARAM_SECTION_NAME(                \
>     +                                 libname, PARAM_SECTION_SUFFIX)  \
>     +                                  );                             \
>     +         if (len > 0) {                                          \
>     +                 _LIB_VARNAME_SET(LIB_PARAMVAR_PREFIX, libname). \
>     +                                 sec_addr_start =                \
>     +                                         (struct uk_param *)     \
>     +                                         ALIGN_UP((__uptr)       \
>     +                                         &_SECTION_START(        \
>     +                                         _LIB_PARAM_SECTION_NAME(\
>     +                                         libname,                \
>     +                                         PARAM_SECTION_SUFFIX)), \
>     +                                         sizeof(void *));        \
>     +                 _LIB_VARNAME_SET(LIB_PARAMVAR_PREFIX, libname). \
>     +                                         len =   len;            \
>     +                 _LIB_VARNAME_SET(LIB_PARAMVAR_PREFIX, libname). \
>     +                                  lib_name =             \
>     +                                         &_LIB_VARNAME_SET(      \
>     +                                         LIB_NAMEVAR_PREFIX,     \
>     +                                         libname)[0];            \
>     +                 _uk_libparam_lib_add(&_LIB_VARNAME_SET(         \
>     +                                         LIB_PARAMVAR_PREFIX,    \
>     +                                         libname)                \
>     +                                     );                          \
>     +         }                                                       \
>     + }                                                               \
>     +
>     +#define UK_LIB_CONSTRUCTOR_INIT(libname)                         \
>     +         UK_LIB_IMPORT_SECTION_PARAMS(libname,                   \
>     +                                      PARAM_SECTION_SUFFIX);     \
>     +         UK_LIB_SECTION_CREATE(UK_LIBPARAM_SECTION, libname);    \
>     +         UK_LIB_CONSTRUCTOR_CREATE(libname)                      \
>     +         UK_LIB_CONSTRUCTOR_SETUP(UK_LIB_CTOR_PRIO,              \
>     +                 _LIB_UK_CONSTRUCT_NAME(libname, process_arg))
>     +
>     +
>     +/**
>     + * Create a constructor to fill in the parameter.
>     + */
>     +#ifdef UK_LIBPARAM_PREFIX
>     + UK_LIB_CONSTRUCTOR_INIT(UK_LIBPARAM_PREFIX);
>     +#endif /* UK_LIBPARAM_PREFIX */
>     +
>     +/**
>     + * Create the fully qualified name of a parameter.
>     + *
>     + * @param libname
>     + *       The name of the library
>     + * @param name
>     + *       The name of the parameter
>     + */
>     +#define _LIB_PARAM_STRING(libname, name)                 \
>     +                 libname.name
>     +
>     +/**
>     + * Initialize the parameter string in a variable. The name of the
>     + * parameter is stored in a separate linker section.
>     + *
>     + * @param name
>     + *       The name of the variable
>     + * @param value
>     + *       The string representation of the parameter.
>     + */
>     +#define _LIB_PARAM_NAME_SET(name, value)                         \
>     + static const                                                    \
>     + char _LIB_VARNAME_SET(PARAM_NAMEVAR_PREFIX, name)[] =           \
>     +                                         __STRINGIFY(value)
>     +
>     +
>     +/**
>     + * Initialize the parameter structure.
>     + *
>     + * @param param_name
>     + *       The name of the parameter
>     + * @param type
>     + *       The type of the parameter
>     + * @param cnt
>     + *       The number of the elements of that type.
>     + */
>     +#define _LIB_UK_PARAM_SET(param_name, type, cnt)                 \
>     + static const                                                    \
>     + _LIB_PARAM_SECTION_ADD(                                         \
>     +                         _LIB_PARAM_SECTION_NAME(                \
>     +                                         UK_LIBPARAM_PREFIX,     \
>     +                                         PARAM_SECTION_SUFFIX),  \
>     +                                         void *                  \
>     +                         )                                       \
>     + struct uk_param _LIB_VARNAME_SET(PARAM_SECTION_SUFFIX,          \
>     +                                  param_name) = {                \
>     +         .name = _LIB_VARNAME_SET(PARAM_NAMEVAR_PREFIX,          \
>     +                                   param_name),                  \
>     +         .param_type = _LIB_PARAM_##type,                        \
>     +         .param_size = cnt,                                      \
>     +         .addr       = (__uptr) &param_name,                     \
>     + }
>     +
>     +/**
>     + * Declare a library param.
>     + * @param name
>     + *       The name of the library param.
>     + * @param type
>     + *       The type of the param.
>     + */
>     +#define UK_LIB_PARAM(name, type)                                 \
>     + _LIB_PARAM_NAME_SET(name, _LIB_PARAM_STRING(UK_LIBPARAM_PREFIX, \
>     +                                             name));             \
>     + _LIB_UK_PARAM_SET(name, type, 1)
>     +#endif /* !__ASSEMBLY__ */
>     +#endif /* CONFIG_LIBUKLIBPARAM */
>     +
>     +#ifndef __ASSEMBLY__
>     +#ifdef __cplusplus
>     +}
>     +#endif /* __cplusplus */
>     +#endif /* !__ASSEMBLY */
>     +
>     +#endif /* __UK_LIBPARAM_H */
>     diff --git a/lib/uklibparam/param.c b/lib/uklibparam/param.c
>     new file mode 100644
>     index 00000000..d11acd3f
>     --- /dev/null
>     +++ b/lib/uklibparam/param.c
>     @@ -0,0 +1,531 @@
>     +/* SPDX-License-Identifier: BSD-3-Clause */
>     +/*
>     + * Authors: Sharan Santhanam <sharan.santhanam@xxxxxxxxx>
>     + *
>     + * Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights 
> reserved.
>     + *
>     + * Redistribution and use in source and binary forms, with or without
>     + * modification, are permitted provided that the following conditions
>     + * are met:
>     + *
>     + * 1. Redistributions of source code must retain the above copyright
>     + *    notice, this list of conditions and the following disclaimer.
>     + * 2. Redistributions in binary form must reproduce the above copyright
>     + *    notice, this list of conditions and the following disclaimer in the
>     + *    documentation and/or other materials provided with the 
> distribution.
>     + * 3. Neither the name of the copyright holder nor the names of its
>     + *    contributors may be used to endorse or promote products derived 
> from
>     + *    this software without specific prior written permission.
>     + *
>     + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
> "AS IS"
>     + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
> THE
>     + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> PURPOSE
>     + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
> CONTRIBUTORS BE
>     + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
>     + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
>     + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
> BUSINESS
>     + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
> IN
>     + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
> OTHERWISE)
>     + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
> OF THE
>     + * POSSIBILITY OF SUCH DAMAGE.
>     + *
>     + * THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY.
>     + */
>     +#include <string.h>
>     +#include <stdlib.h>
>     +#include <stdio.h>
>     +#include <errno.h>
>     +#include <uk/list.h>
>     +#include <uk/arch/limits.h>
>     +#include <uk/print.h>
>     +#include <uk/assert.h>
>     +#include <uk/libparam.h>
>     +#include <uk/version.h>
>     +
>     +#define LIB_ARG_SEP       "--"
>     +#define NUMBER_SET(fn, type, value, addr, max, min, errcode, 
> result_type, fmt)\
>     + do {                                                            \
>     +         errno = 0;                                              \
>     +         result_type result = (result_type)fn(value, NULL, 10);  \
>     +         unsigned long long maxvalue =                           \
>     +                         (sizeof(type) == sizeof(maxvalue)) ?    \
>     +                         (result_type)-1 :                       \
>     +                         (1ULL << ((sizeof(type) << 3))) - 1;    \
>     +         uk_pr_debug("max value: 0x%llx\n", maxvalue);           \
>     +         if (errno != 0)                                         \
>     +                 errcode = -errno;                               \
>     +         else if (result >= maxvalue) {                          \
>     +                 errcode = 1;                                    \
>     +                 *((type *)addr) = (type)(result & maxvalue);    \
>     +         } else {                                                \
>     +                 errcode = 0;                                    \
>     +                 *((type *)addr) = (type)(result & maxvalue);    \
>     +         }                                                       \
>     +         uk_pr_debug("Converting value %s to %"fmt" %"fmt"\n",   \
>     +                     value, *(type *)addr, result);              \
>     + } while (0)
>     +
>     +struct param_args {
>     + /* Reference to the start of the library */
>     + char *lib;
>     + /* Reference to the start of the parameter */
>     + char *param;
>     + /* Reference to the start of the value */
>     + char *value;
>     + /* length of the library name */
>     + __u32 lib_len;
>     + /* length of the parameter */
>     + __u32 param_len;
>     + /* length of the value */
>     + __u32 value_len;
>     +};
>     +
>     +static UK_LIST_HEAD(uk_libsections);
>     +
>     +/**
>     + * Local functions
>     + */
>     +static int kernel_arg_range_fetch(int argc, char **argv);
>     +static void uk_usage(const char *progname);
>     +static int kernel_arg_fetch(char **args, int nr_args,
>     +                     struct param_args *pargs, int *rewind);
>     +static int kernel_lib_fetch(struct param_args *pargs,
>     +                     struct uk_lib_section **section);
>     +static int kernel_parse_arg(struct param_args *pargs,
>     +                     struct uk_lib_section *section,
>     +                     struct uk_param **param);
>     +static int kernel_arg_set(void *addr, char *value, int size, int sign);
>     +static int kernel_args_set(struct param_args *pargs,
>     +                    struct uk_param *param);
>     +static int kernel_value_sanitize(struct param_args *pargs);
>     +
>     +void _uk_libparam_lib_add(struct uk_lib_section *lib_sec)
>     +{
>     + uk_pr_info("libname: %s, %d\n", lib_sec->lib_name, lib_sec->len);
>     + uk_list_add_tail(&lib_sec->next, &uk_libsections);
>     +}
>     +
>     +static void uk_usage(const char *progname)
>     +{
>     + printf("Usage: %s\n", progname);
>     + printf(" [[UNIKRAFT KERNEL ARGUMENT]].. -- [[APPLICATION 
> ARGUMENT]]..\n\n");
>     + printf("Unikraft library arguments:\n");
>     + printf("The library arguments are represented as 
> [LIBPARAM_PREFIX].[PARAMNAME]\n\n");
>     + printf("  -h, --help                 display this help and exit\n");
>     + printf("  -V, --version              display Unikraft version and 
> exit\n");
>     +}
>     +
>     +static int kernel_arg_range_fetch(int argc, char **argv)
>     +{
>     + int i = 0;
>     +
>     + while (i < argc) {
>     +         /* Separate the kernel param from the application parameters */
>     +         if (strcmp(LIB_ARG_SEP, argv[i]) == 0)
>     +                 return i;
>     +         i++;
>     + }
>     +
>     + return -1;
>     +}
>     +
>     +static int kernel_arg_fetch(char **args, int nr_args,
>     +                     struct param_args *pargs, int *rewind)
>     +{
>     + int i = 0;
>     + int rc = 0;
>     + char *equals_ptr = NULL, *dupl_ptr = NULL;
>     + int len, cnt = 0, equals = -1;
>     +
>     + UK_ASSERT(rewind && pargs);
>     +
>     + pargs->param = NULL;
>     + pargs->value = NULL;
>     + pargs->param_len = 0;
>     + pargs->value_len = 0;
>     +
>     + for (i = 0; (!pargs->value_len ||
>     +              !pargs->param_len) && i < nr_args; i++) {
>     +         uk_pr_debug("at index:%d user args %s\n", i, args[i]);
>     +         len = strlen(args[i]);
>     +         /* if the equals character is present */
>     +         if (!equals_ptr)
>     +                 equals_ptr = strchr(args[i], '=');
>     +         cnt++;
>     +         /* Check for multiple '=' */
>     +         dupl_ptr = strrchr(args[i], '=');
>     +         if (equals_ptr && dupl_ptr && equals_ptr !=  dupl_ptr) {
>     +                 uk_pr_err("Multiple '=' character found. Skipping 
> argument %s\n",
>     +                            args[i]);
>     +                 rc = -EINVAL;
>     +                 goto exit;
>     +         } else if (equals < 0) {
>     +                 /* Searching for the parameters */
>     +                 if (equals_ptr && (len > 1) &&
>     +                    (equals_ptr - args[i]) == (len - 1)) {
>     +                         /* [libname_prefix].[parameter]= value */
>     +                         uk_pr_debug("Expecting parameter with equals 
> %s\n",
>     +                                      args[i]);
>     +                         pargs->param = args[i];
>     +                         pargs->param_len = len - 1;
>     +                         equals = i;
>     +                 } else if (equals_ptr && (len > 1) &&
>     +                            equals_ptr == args[i]) {
>     +                         /* [libname_prefix].[parameter] =value */
>     +                         uk_pr_debug("Expecting equals followed by value 
> %s\n",
>     +                                     args[i]);
>     +                         pargs->value =  equals_ptr + 1;
>     +                         pargs->value_len = len - 1;
>     +                         equals = i;
>     +                 } else if (equals_ptr && len == 1) {
>     +                         /* Contains only equals */
>     +                         equals = i;
>     +                         continue;
>     +                 } else if (equals_ptr) {
>     +                         /* [libname_prefix].[parameter]=value */
>     +                         uk_pr_debug("Expecting entire argument %s\n",
>     +                                     args[i]);
>     +                         pargs->param = args[i];
>     +                         pargs->param_len = equals_ptr - args[i];
>     +                         equals = i;
>     +                         pargs->value = equals_ptr + 1;
>     +                         pargs->value_len = len - (pargs->param_len + 1);
>     +                 } else if (!equals_ptr) {
>     +                         /* [libname_prefix].[parameter] = value */
>     +                         uk_pr_debug("Expecting parameter alone%s\n",
>     +                                     args[i]);
>     +                         pargs->param = args[i];
>     +                         pargs->param_len = len;
>     +                 } else {
>     +                         uk_pr_err("Failed to parse the argument %s\n",
>     +                                   args[i]);
>     +                         rc = -EINVAL;
>     +                         goto exit;
>     +                 }
>     +         } else if (equals >= 0) {
>     +                 uk_pr_debug("Expecting value only %s\n",
>     +                             args[i]);
>     +                 pargs->value = args[i];
>     +                 pargs->value_len = len;
>     +         } else {
>     +                 /* Error case */
>     +                 uk_pr_err("Failed to parse the argument:%s\n", args[i]);
>     +                 rc = -EINVAL;
>     +                 goto exit;
>     +
>     +         }
>     + }
>     +
>     + uk_pr_debug("pargs->param: %p, pargs->value: %p\n", pargs->param,
>     +             pargs->value);
>     + if (pargs->param_len != 0 && pargs->value_len == 0) {
>     +         uk_pr_err("Failed to completely parse the user argument\n");
>     +         rc = -EINVAL;
>     +         goto exit;
>     + }
>     +
>     +exit:
>     + *rewind = cnt;
>     + return rc;
>     +}
>     +
>     +/**
>     + * Kernel Parameter are passed in this format
>     + * [libname_prefix].[parameter]
>     + */
>     +static int kernel_lib_fetch(struct param_args *pargs,
>     +                     struct uk_lib_section **section)
>     +{
>     + char *libparam;
>     + struct uk_lib_section *iter;
>     +
>     + UK_ASSERT(section && pargs);
>     + pargs->lib_len = 0;
>     + libparam = memchr(pargs->param, '.', pargs->param_len);
>     + if (!libparam) {
>     +         uk_pr_err("Failed to identify the library\n");
>     +         goto error_exit;
>     + }
>     +
>     + uk_list_for_each_entry(iter, &uk_libsections, next) {
>     +         uk_pr_debug("Lib: %s, libname: %s %ld\n", iter->lib_name,
>     +                     pargs->param, libparam - pargs->param);
>     +         /**
>     +          * Compare the length of the library names to avoid having
>     +          * library with a similar prefix wrongly matching.
>     +          */
>     +         if ((strlen(iter->lib_name) ==
>     +             (size_t) (libparam - pargs->param)) &&
>     +             memcmp(pargs->param, iter->lib_name,
>     +                    (libparam - pargs->param)) == 0) {
>     +                 *section = iter;
>     +                 pargs->lib_len = libparam - pargs->param;
>     +                 return 0;
>     +         }
>     + }
>     + uk_pr_err("Failed to fetch the library\n");
>     +
>     +error_exit:
>     + *section = NULL;
>     + pargs->lib_len = 0;
>     + return -EINVAL;
>     +}
>     +
>     +static int kernel_parse_arg(struct param_args *pargs,
>     +                     struct uk_lib_section *section,
>     +                     struct uk_param **param)
>     +{
>     + int i = 0;
>     + struct uk_param *iter;
>     + int len = 0;
>     +
>     + UK_ASSERT(section && param && pargs);
>     +
>     + len = section->len / sizeof(struct uk_param);
>     + iter = section->sec_addr_start;
>     + uk_pr_debug("Section length %d section@%p, uk_param: %lu\n", len, iter,
>     +             sizeof(*iter));
>     +
>     + for (i = 0; i < len; i++, iter++) {
>     +         UK_ASSERT(iter->name);
>     +         uk_pr_debug("Param name: %s at address: %p\n", iter->name,
>     +                     iter);
>     +         /**
>     +          * Compare the length of the library names to avoid having
>     +          * library with a similar prefix wrongly matching.
>     +          */
>     +         if ((strlen(iter->name) == pargs->param_len) &&
>     +              memcmp(iter->name, pargs->param, pargs->param_len) == 0) {
>     +                 *param = iter;
>     +                 return 0;
>     +         }
>     + }
>     +
>     + uk_pr_err("Failed to identify the parameter\n");
>     + *param = NULL;
>     + return -EINVAL;
>     +}
>     +
>     +static int kernel_arg_set(void *addr, char *value, int size, int sign)
>     +{
>     + int error = 0;
>     +
>     + /**
>     +  * Check for the output address instead of UK_ASSERT because this is
>     +  * a user provided input.
>     +  */
>     + if (!addr) {
>     +         uk_pr_err("Invalid output buffer\n");
>     +         goto error_exit;
>     + }
>     +
>     + switch (size) {
>     + case 1:
>     +         if (sign) {
>     +                 *((__s8 *)addr) = *value;
>     +                 if (strnlen(value, 2) > 1)
>     +                         error = 1;
>     +         } else
>     +                 NUMBER_SET(strtoul, __u8, value, addr, __U8_MAX,
>     +                            __U8_MIN, error, __u32, __PRIu8);
>     +         break;
>     + case 2:
>     +         if (sign)
>     +                 NUMBER_SET(strtol, __s16, value, addr, __S16_MAX,
>     +                            __S16_MIN, error, __u32, __PRIs16);
>     +         else
>     +                 NUMBER_SET(strtoul, __u16, value, addr, __U16_MAX,
>     +                            __U16_MIN, error, __u32, __PRIu16);
>     +         break;
>     + case 4:
>     +         if (sign)
>     +                 NUMBER_SET(strtol, __s32, value, addr, __S32_MAX,
>     +                            __S32_MIN, error, __u32, __PRIs32);
>     +         else
>     +                 NUMBER_SET(strtoul, __u32, value, addr, __U32_MAX,
>     +                            __U32_MIN, error, __u32, __PRIu32);
>     +         break;
>     + case 8:
>     +         if (sign)
>     +                 NUMBER_SET(strtoll, __s64, value, addr, __S64_MAX,
>     +                            __S64_MIN, error, __u64, __PRIs64);
>     +         else
>     +                 NUMBER_SET(strtoull, __u64, value, addr, __U64_MAX,
>     +                            __U64_MIN, error, __u64, __PRIu64);
>     +         break;
>     + default:
>     +         uk_pr_err("Cannot understand type of size %d\n", size);
>     +         goto error_exit;
>     + }
>     + if (error < 0)
>     +         goto error_exit;
>     + else if (error == 1)
>     +         uk_pr_warn("Overflow/Underflow detected in value %s\n", value);
>     + return 0;
>     +
>     +error_exit:
>     + uk_pr_err("Failed to convert value %s\n", value);
>     + return -EINVAL;
>     +}
>     +
>     +static int kernel_args_set(struct param_args *pargs,
>     +                    struct uk_param *param)
>     +{
>     + int rc = 0;
>     + int sign = (param->param_type >> PARAM_SIGN_SHIFT) & PARAM_SIGN_MASK;
>     + int scopy = (param->param_type >> PARAM_SCOPY_SHIFT) & PARAM_SCOPY_MASK;
>     + int param_type = (param->param_type >> PARAM_SIZE_SHIFT)
>     +                         & PARAM_SIZE_MASK;
>     + uk_pr_debug("Parameter value %s, type: %d, sign: %d scopy: %d\n",
>     +             pargs->value, param_type, sign, scopy);
>     +
>     + if (scopy == 1)
>     +         /* Reference the pointer instead of copying the value */
>     +         *((__uptr *)param->addr) = (__uptr) pargs->value;
>     + else {
>     +         if (param->param_size == 1) {
>     +                 rc = kernel_arg_set((void *)param->addr,
>     +                                     pargs->value, param_type, sign);
>     +         } else {
>     +                 uk_pr_err("Error: Cannot find the parameter\n");
>     +                 rc = -EINVAL;
>     +         }
>     + }
>     +
>     + return rc;
>     +}
>     +
>     +/**
>     + * The function removes parse for quotes around the value.
>     + * TODO: We do not support nested '"'.
>     + */
>     +static int kernel_value_sanitize(struct param_args *pargs)
>     +{
>     + int rc = 0;
>     + char *ptr;
>     + char *start_idx = NULL;
>     + char *end_idx = NULL;
>     + int qcnt = 0;
>     +
>     + UK_ASSERT(pargs && pargs->value);
>     + ptr = pargs->value;
>     + uk_pr_debug("Sanitizing value %s (length %d)\n", pargs->value,
>     +             pargs->value_len);
>     +
>     + do {
>     +         switch (*ptr) {
>     +         case ' ':
>     +         case '\r':
>     +         case '\n':
>     +         case '\t':
>     +         case '\v':
>     +                 ptr++;
>     +                 break;
>     +         case'\'':
>     +         case '"':
>     +                 if (start_idx)
>     +                         end_idx = ptr;
>     +                 else if (!start_idx)
>     +                         start_idx = ptr + 1;
>     +                 ptr++;
>     +                 qcnt++;
>     +                 break;
>     +         default:
>     +                 if (!start_idx)
>     +                         start_idx = ptr;
>     +                 ptr++;
>     +                 break;
>     +         }
>     + } while (*ptr != '\0' && !(end_idx && start_idx));
>     + if (!end_idx)
>     +         end_idx =  ptr;
>     +
>     + uk_pr_debug("Adjusting start to %p & end to %p #quotes: %d\n",
>     +             start_idx, end_idx, qcnt);
>     +
>     + if (qcnt == 1) {
>     +         uk_pr_err("Value %s not quoted properly\n", pargs->value);
>     +         rc = -EINVAL;
>     + } else if (start_idx && end_idx) {
>     +         memset(pargs->value, '\0', start_idx - pargs->value);
>     +         memset(end_idx, '\0',
>     +                (pargs->value + pargs->value_len) - end_idx);
>     +         pargs->value = start_idx;
>     +         pargs->value_len = end_idx - start_idx;
>     + }
>     + uk_pr_debug("Sanitized value %s (length %d)\n", pargs->value,
>     +             pargs->value_len);
>     +
>     + return rc;
>     +}
>     +
>     +int uk_libparam_parse(const char *progname, int argc, char **argv)
>     +{
>     + int keindex = 0;
>     + int rc = 0, cnt = 0, args_read;
>     + struct param_args pargs = {0};
>     + struct uk_lib_section *section = NULL;
>     + struct uk_param *param = NULL;
>     +
>     + keindex = kernel_arg_range_fetch(argc, argv);
>     + if (keindex < 0) {
>     +         uk_pr_info("No library arguments found\n");
>     +         return 0;
>     + }
>     +
>     + uk_pr_debug("Library argument ends at %d\n", keindex);
>     +
>     + while (cnt < keindex) {
>     +         /* help and version */
>     +         if (strcmp(argv[cnt], "-h") == 0 ||
>     +             strcmp(argv[cnt], "--help") == 0) {
>     +                 uk_usage(progname);
>     +                 ukplat_halt();
>     +         } else if (strcmp(argv[cnt], "-V") == 0 ||
>     +                    strcmp(argv[cnt], "--version") == 0) {
>     +                 uk_version();
>     +                 ukplat_halt();
>     +         }
>     +
>     +         args_read = 0;
>     +         /* Fetch the argument from the input */
>     +         rc = kernel_arg_fetch(&argv[cnt], (keindex - cnt),
>     +                               &pargs, &args_read);
>     +         if (rc < 0) {
>     +                 uk_pr_err("Failed to fetch arg between index %d and 
> %d\n",
>     +                           cnt, (cnt + args_read));
> 
> Just to make this a bit more user friendly, it would be good to print out the 
> actual argument, not just the index. This would be particularly useful when 
> providing a large list of args.
> 
> 
>     +                 cnt += args_read;
>     +                 continue;
>     +         }
>     +         uk_pr_debug("Processing argument %s\n", pargs.param);
>     +         cnt += args_read;
>     +
>     +         /* Fetch library for the argument */
>     +         rc = kernel_lib_fetch(&pargs, &section);
>     +         if (rc < 0 || !section) {
>     +                 uk_pr_err("Failed to identify the library\n");
>     +                 continue;
>     +         }
>     +
>     +         /* Fetch the parameter for the argument */
>     +         rc = kernel_parse_arg(&pargs, section, &param);
>     +         if (rc < 0 || !param) {
>     +                 uk_pr_err("Failed to parse arg\n");
>     +                 continue;
>     +         }
>     +
>     +         rc = kernel_value_sanitize(&pargs);
>     +         if (rc  < 0) {
>     +                 uk_pr_err("Failed to sanitize %s param\n", pargs.param);
>     +                 continue;
>     +         }
>     +
>     +         rc = kernel_args_set(&pargs, param);
>     +         uk_pr_info("Parsed %d args\n", cnt);
>     + }
>     +
>     + /* Replacing the -- with progname */
>     + argv[keindex] = DECONST(char *, progname);
>     +
>     + return keindex + 1;
>     +}
>     -- 
>     2.20.1
>     
>     
>     _______________________________________________
>     Minios-devel mailing list
>     Minios-devel@xxxxxxxxxxxxxxxxxxxx
>     https://lists.xenproject.org/mailman/listinfo/minios-devel
> 
> _______________________________________________
> Minios-devel mailing list
> Minios-devel@xxxxxxxxxxxxxxxxxxxx
> https://lists.xenproject.org/mailman/listinfo/minios-devel
> 

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

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