/* defined in <uk/plat.h> */
void ukplat_entry(int argc, char *argv[])
{
- int kern_args = 0;
int rc = 0;
#if CONFIG_LIBUKALLOC
struct uk_alloc *a = NULL;
}
#ifdef CONFIG_LIBUKLIBPARAM
- rc = (argc > 1) ? uk_libparam_parse(argv[0], argc - 1, &argv[1]) : 0;
- if (unlikely(rc < 0))
- uk_pr_crit("Failed to parse the kernel argument\n");
- else {
- kern_args = rc;
- uk_pr_info("Found %d library args\n", kern_args);
+ /*
+ * First, we scan if we can find the stop sequence in the kernel
+ * cmdline. If not, we assume that there are no uklibparam arguments in
+ * the command line.
+ * NOTE: argv[0] contains the kernel/program name that we need to
+ * hide from the parser.
+ */
+ rc = uk_libparam_parse(argc - 1, &argv[1], UK_LIBPARAM_F_SCAN);
+ if (rc > 0 && rc < (argc - 1)) {
+ /* In this case, we did successfully scan for uklibparam
+ * arguments and stop sequence is at rc < (argc - 1).
+ */
+ /* Run a second pass for parsing */
+ rc = uk_libparam_parse(argc - 1, &argv[1], UK_LIBPARAM_F_USAGE);
+ if (rc < 0) /* go down on errors (including USAGE) */
+ ukplat_halt();
+
+ /* Drop uklibparam parameters from argv but keep argv[0].
+ * We are going to replace the stop sequence with argv[0].
+ */
+ rc += 1; /* include argv[0]; we use rc as idx to stop seq */
+ argc -= rc;
+ argv[rc] = argv[0];
+ argv = &argv[rc];
}
#endif /* CONFIG_LIBUKLIBPARAM */
uk_sched_start(s);
#endif /* !CONFIG_LIBUKBOOT_NOSCHED */
- argc -= kern_args;
- argv = &argv[kern_args];
-
/* Enable interrupts before starting the application */
ukplat_lcpu_enable_irq();
-# add_paramprefix $name $libname
-define _add_paramprefix =
-$(eval $(call uc,$(2))_CFLAGS += -DUK_LIBPARAM_PREFIX=$(1))
-$(eval $(call uc,$(2))_CXXFLAGS += -DUK_LIBPARAM_PREFIX=$(1))
-$(eval $(call uc,$(2))_ASFLAGS += -DUK_LIBPARAM_PREFIX=$(1))
-$(eval $(call uc,$(2))_SRCS-$(CONFIG_LIBUKLIBPARAM) += $$(LIBUKLIBPARAM_BASE)/libparam.lds.S)
+define _uk_libparam_libprefix_set =
+$(eval $(call uc,$(2))_CFLAGS += -DUK_LIBPARAM_LIBPREFIX=$(1))
+$(eval $(call uc,$(2))_CXXFLAGS += -DUK_LIBPARAM_LIBPREFIX=$(1))
+$(eval $(call uc,$(2))_ASFLAGS += -DUK_LIBPARAM_LIBPREFIX=$(1))
endef
-# addlib_paramprefix $libname,$paramname(optional)
-define addlib_paramprefix =
+# Set an alternative library prefix for library parameters
+# uk_libparam_prefix_set 1:library_name, 2:new_prefix
+define uk_libparam_libprefix_set =
$(if $(2),\
$(eval name := $(2)),\
$(eval name := $(1)))
-$(eval $(call _add_paramprefix,$(name),$(1),$(call uc,$(1))))
+$(eval $(call _uk_libparam_libprefix_set,$(name),$(1),$(call uc,$(1))))
+endef
+
+# Deprecated registration function
+# WARNING: This interface is here for backwards compatibility and will be
+# removed in the near future.
+define addlib_paramprefix =
+$(call uk_libparam_libprefix_set,$(1),$(2))
endef
CINCLUDES-y += -I$(LIBUKLIBPARAM_BASE)/include
CXXINCLUDES-y += -I$(LIBUKLIBPARAM_BASE)/include
-LIBUKLIBPARAM_SRCS-y += $(LIBUKLIBPARAM_BASE)/param.c
+LIBUKLIBPARAM_SRCS-y += $(LIBUKLIBPARAM_BASE)/parser.c
+
+# Push linker script to each library for making the section available
+EACHOLIB_SRCS-$(CONFIG_LIBUKLIBPARAM) += $(LIBUKLIBPARAM_BASE)/libparam.lds.S|uklibparam
uk_libparam_parse
-_uk_libparam_lib_add
+_uk_libparam_libsec_register
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Authors: Sharan Santhanam <sharan.santhanam@neclab.eu>
+ * Simon Kuenzer <simon@unikraft.io>
*
* Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ * Copyright (c) 2023, Unikraft GmbH. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#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.
+/* We need to define an own version here in order to avoid (indirect) header
+ * dependencies in our linker script `libparam.lds.S`. These headers are likely
+ * not prepared or available in the scope of linker scripts.
*/
-#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 _UK_LIBPARAM_CONCAT(a, b) a ## b
+#define UK_LIBPARAM_CONCAT(a, b) _UK_LIBPARAM_CONCAT(a, b)
-#define __STRINGCONCAT(x, y) x ## y
+/* Fallback to default library prefix if not set by compiler flag */
+#ifndef UK_LIBPARAM_LIBPREFIX
+#define UK_LIBPARAM_LIBPREFIX __LIBNAME__
+#endif /* !UK_LIBPARAM_PREFIX */
-/**
- * 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)
-
-/**
- * Declare a string library param. This is a dummy implementation.
- * @param name
- * The name of the parameter.
- */
-#define UK_LIB_PARAM_STR(name)
-
-/**
- * Declare an array of primitive.
- * @param name
- * The name of the parameter.
- * @param type
- * The type of the parameter.
- */
-#define UK_LIB_PARAM_ARR(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)
+/*
+ * Name (prefixes) used for a per-library section that stores all references
+ * to parameters (struct uk_libparam_param) available for one library. This
+ * section is basically compiles into an array of pointers.
+ * A library parameter descriptor (struct uk_libparam_desc) is referencing to
+ * the section's base address for iteration.
+ */
+#define UK_LIBPARAM_PARAMSECTION_NAMEPREFIX uk_libparam_params_
+#define UK_LIBPARAM_PARAM_NAMEPREFIX __uk_libparam_param_
+
+#define UK_LIBPARAM_PARAMSECTION_NAME \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAMSECTION_NAMEPREFIX, __LIBNAME__)
+#define UK_LIBPARAM_PARAMSECTION_STARTSYM \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAMSECTION_NAME, _start)
+#define UK_LIBPARAM_PARAMSECTION_ENDSYM \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAMSECTION_NAME, _end)
#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.
+#ifdef CONFIG_LIBUKLIBPARAM
+/* The following symbols are provided by ther per-library linker script. They
+ * They define the start and the end of the library parameters reference array
*/
-#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)) \
- )
+extern struct uk_libparam_param * const UK_LIBPARAM_PARAMSECTION_STARTSYM[];
+extern struct uk_libparam_param * const UK_LIBPARAM_PARAMSECTION_ENDSYM;
-/**
- * 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))
-#define _LIB_PARAM___uptr PARAM_TYPE(0, 1, sizeof(__uptr))
-#define _LIB_PARAM_charp _LIB_PARAM___uptr
-
-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;
+/*
+ * Library parameter data types
+ */
+enum uk_libparam_param_type {
+ _UK_LIBPARAM_PARAM_TYPE___undef = 0,
+ _UK_LIBPARAM_PARAM_TYPE_bool,
+ _UK_LIBPARAM_PARAM_TYPE___s8,
+#define _UK_LIBPARAM_PARAM_TYPE_char _UK_LIBPARAM_PARAM_TYPE___s8
+ _UK_LIBPARAM_PARAM_TYPE___u8,
+#define _UK_LIBPARAM_PARAM_TYPE_uchar _UK_LIBPARAM_PARAM_TYPE___u8
+ _UK_LIBPARAM_PARAM_TYPE___s16,
+ _UK_LIBPARAM_PARAM_TYPE___u16,
+ _UK_LIBPARAM_PARAM_TYPE___s32,
+#define _UK_LIBPARAM_PARAM_TYPE_int _UK_LIBPARAM_PARAM_TYPE___s32
+ _UK_LIBPARAM_PARAM_TYPE___u32,
+#define _UK_LIBPARAM_PARAM_TYPE_uint _UK_LIBPARAM_PARAM_TYPE___u32
+ _UK_LIBPARAM_PARAM_TYPE___s64,
+ _UK_LIBPARAM_PARAM_TYPE___u64,
+ _UK_LIBPARAM_PARAM_TYPE___uptr,
+ _UK_LIBPARAM_PARAM_TYPE_charp
};
-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;
+/*
+ * Library parameter descriptor
+ */
+struct uk_libparam_param {
+ const char * const name;
+ const char * const desc;
+ const enum uk_libparam_param_type type;
+ /* Number of elements (>1 means we have an array of the given type) */
+ const __sz count;
+ /* Reference to corresponding variable */
+ void * const addr;
+
+ /* Internally use by parser for array parameters */
+ __sz __widx;
};
-/**
- * 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)) \
+/*
+ * Library descriptor
+ */
+struct uk_libparam_libdesc {
+ /* Library prefix */
+ const char *prefix;
+ /* Section header of parameter reference array */
+ struct uk_libparam_param **params;
+ /* Number of parameters */
+ __sz params_len;
+ /* Next libdesc entry */
+ struct uk_list_head next;
+};
-/**
- * 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_PRIO(name, prio)
+/* -------------------------------------------------------------------------- */
+/* Library registration */
+
+/* The following code declares a library descriptor as soon as this header
+ * is included: This makes an explicit registration to libuklibparam
+ * unnecessary.
+ * FIXME: This allows using of library parameters only in one source file of a
+ * library. A solution could be to create a file in uklibparam that
+ * declares an array of structs, one struct per library. Ideally the
+ * fields are filled out at compile/link time. This can be done as soon
+ * as a library is available that can export the the list of included
+ * libraries of a build.
+ * NOTE: Double declaration within one source file is avoided by the header's
+ * include guards.
+ */
+#define UK_LIBPARAM_LIBDESC \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAMSECTION_NAME, _libdesc)
+#define UK_LIBPARAM_LIBDESC_CTOR \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAMSECTION_NAME, _ctor)
+
+static struct uk_libparam_libdesc UK_LIBPARAM_LIBDESC = {
+ .prefix = STRINGIFY(UK_LIBPARAM_LIBPREFIX)
+
+ /* Rest of fields are initialized with 0 automatically */
+};
-/**
- * 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))
+void _uk_libparam_libsec_register(struct uk_libparam_libdesc *ld);
+static void UK_LIBPARAM_LIBDESC_CTOR(void)
+{
+ UK_LIBPARAM_LIBDESC.params_len =
+ (__sz)((__uptr) &UK_LIBPARAM_PARAMSECTION_ENDSYM -
+ (__uptr) UK_LIBPARAM_PARAMSECTION_STARTSYM) /
+ sizeof(void *);
-/**
- * Create a constructor to fill in the parameter.
- */
-#ifdef UK_LIBPARAM_PREFIX
- UK_LIB_CONSTRUCTOR_INIT(UK_LIBPARAM_PREFIX);
-#endif /* UK_LIBPARAM_PREFIX */
+ if (UK_LIBPARAM_LIBDESC.params_len > 0) {
+ UK_LIBPARAM_LIBDESC.params = (struct uk_libparam_param **)
+ UK_LIBPARAM_PARAMSECTION_STARTSYM;
+ _uk_libparam_libsec_register(&UK_LIBPARAM_LIBDESC);
+ }
+}
+UK_CTOR_PRIO(UK_LIBPARAM_LIBDESC_CTOR, 0);
+
+/* -------------------------------------------------------------------------- */
+/* Parameter registration */
+
+#define __UK_LIBPARAM_PARAM_NAME(varname) \
+ UK_LIBPARAM_CONCAT(UK_LIBPARAM_PARAM_NAMEPREFIX, varname)
+
+#define __UK_LIBPARAM_PARAM_DEFINE(arg_var, arg_addr, arg_type, arg_count, \
+ arg_desc) \
+ static struct uk_libparam_param __UK_LIBPARAM_PARAM_NAME(arg_var) = { \
+ .name = STRINGIFY(arg_var), \
+ .desc = arg_desc, \
+ .type = _UK_LIBPARAM_PARAM_TYPE_##arg_type, \
+ .count = arg_count, \
+ .addr = arg_addr \
+ }; \
+ \
+ static __section("." STRINGIFY(UK_LIBPARAM_PARAMSECTION_NAME)) \
+ __used struct uk_libparam_param * const \
+ UK_LIBPARAM_CONCAT(__UK_LIBPARAM_PARAM_NAME(arg_var), _ptr) = \
+ &__UK_LIBPARAM_PARAM_NAME(arg_var)
+
+#define _UK_LIBPARAM_PARAM_DEFINE(name, var, type, count, desc) \
+ __UK_LIBPARAM_PARAM_DEFINE(name, var, type, count, desc)
+
+/* -------------------------------------------------------------------------- */
+/* Parser */
-/**
- * Create the fully qualified name of a parameter.
+/*
+ * Flag bits for defining parsing behavior
+ */
+/* Scan only, do not parse and set values */
+#define UK_LIBPARAM_F_SCAN 0x1
+/* Don't skip on unknown arguments, exit with a parsing error */
+#define UK_LIBPARAM_F_STRICT 0x2
+/* Print usage when 'help' is found as parameter and return with -EINTR */
+#define UK_LIBPARAM_F_USAGE 0x4
+
+/**
+ * Parse given parameter list. The parsing mode can be defined with flags
+ * (see: `UK_LIBPARAM_F_*`). Parsing will stop if the end of the argument list
+ * is reached or if the stop sequence `---` is detected.
+ * NOTE: The parser is designed to be alloc-free, errno-free, and TLS-free so
+ * that it can be used in early boot code. Because of this, please note
+ * that registered parameters that expect a C string will be filled with
+ * a reference to the according argv object. argv must not be free'd
+ * after the parser was called (except scan mode).
*
- * @param libname
- * The name of the library
- * @param name
- * The name of the parameter
+ * @param argc
+ * The number of arguments
+ * @param argv
+ * Reference to the command line arguments
+ * NOTE: In strict mode, program name (typically `argv[0]`) should not be
+ * handed over because the parser tries to parse `argv[0]` as well.
+ * @param flags
+ * UK_LIBPARAM_F_* to influence the behavior
+ * @return
+ * On success, the argument index is return where the parser stopped.
+ * A negative errno code is returned (<0) on failure.
*/
-#define _LIB_PARAM_STRING(libname, name) \
- libname.name
+int uk_libparam_parse(int argc, char **argv, int flags);
-/**
- * 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.
+#else /* !CONFIG_LIBUKLIBPARAM */
+
+/* Removes library parameter instrumentation if the library is unselected
+ * WARNING: Do not use directly.
*/
-#define _LIB_PARAM_NAME_SET(name, value) \
- static const \
- char _LIB_VARNAME_SET(PARAM_NAMEVAR_PREFIX, name)[] = \
- __STRINGIFY(value)
+#define _UK_LIBPARAM_PARAM_DEFINE(name, var, type, count, desc)
+#endif /* !CONFIG_LIBUKLIBPARAM */
-/**
- * Initialize the parameter structure.
+/*
+ * Register a single parameter
*
- * @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) ¶m_name, \
- }
-
-/**
- * Declare a library param.
- * @param name
- * The name of the library param.
+ * @param var
+ * Variable to export as library parameter
* @param type
- * The type of the param.
+ * Data type: bool, char, uchar, int, uint, charp, __s8, __u8, __s16,
+ * __u16, __s32, __u32, __s64, __u64, __uptr
+ * @param desc
+ * C string with parameter description. Optional, can be __NULL.
*/
-#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)
+#define UK_LIBPARAM_PARAM(var, type, desc) \
+ _UK_LIBPARAM_PARAM_DEFINE(var, &var, type, 1, desc)
-/**
- * Declare an array of primitive.
- * @param name
- * The name of the parameter.
+/*
+ * Register a parameter array
+ *
+ * @param var
+ * Array to export as library parameter
* @param type
- * The type of the parameter.
- */
-#define UK_LIB_PARAM_ARR(name, type) \
- _LIB_PARAM_NAME_SET(name, _LIB_PARAM_STRING(UK_LIBPARAM_PREFIX, \
- name)); \
- _LIB_UK_PARAM_SET(name, type, sizeof(name)/sizeof(type)) \
-
-/**
- * Declare a string library param.
- * @param name
- * The name of the parameter.
- */
-#define UK_LIB_PARAM_STR(name) \
- UK_LIB_PARAM(name, __uptr)
+ * Data type of array elements: bool, char, uchar, int, uint, charp, __s8,
+ * __u8, __s16, __u16, __s32, __u32, __s64,
+ * __u64, __uptr
+ * @param count
+ * Number of elements in the array that can be filled (<= array size)
+ * @param desc
+ * C string with parameter description. Optional, can be __NULL.
+ */
+#define UK_LIBPARAM_PARAM_ARR(var, type, count, desc) \
+ _UK_LIBPARAM_PARAM_DEFINE(var, &var, type, (count), desc)
+
+/* Deprecated registration macros */
+/* WARNING: These interfaces are here for backwards compatibility and will be
+ * removed in the near future.
+ */
+#define UK_LIB_PARAM(var, type) \
+ _UK_LIBPARAM_PARAM_DEFINE(var, &var, type, 1, __NULL)
+#define UK_LIB_PARAM_STR(var) \
+ UK_LIB_PARAM(var, charp)
+
+#define UK_LIB_PARAM_ARR(var, type) \
+ _UK_LIBPARAM_PARAM_DEFINE(var, &var, type, ARRAY_SIZE(var), __NULL)
+#define UK_LIB_PARAM_ARR_STR(var) \
+ UK_LIB_PARAM_ARR(var, charp)
-#endif /* !__ASSEMBLY__ */
-#endif /* CONFIG_LIBUKLIBPARAM */
-
-#ifndef __ASSEMBLY__
+#endif /* !__ASSEMBLY */
#ifdef __cplusplus
}
#endif /* __cplusplus */
-#endif /* !__ASSEMBLY */
-
#endif /* __UK_LIBPARAM_H */
-#include <uk/config.h>
#include <uk/libparam.h>
-#define create_var(x,y) __STRINGCONCAT(x,y)
-#ifdef UK_LIBPARAM_PREFIX
-SECTIONS
-{
- /**
- * creates a variable name = LOADADDR(section_name);
- */
- _SECTION_START(
- _LIB_PARAM_SECTION_NAME(UK_LIBPARAM_PREFIX,
- PARAM_SECTION_SUFFIX)
- ) = LOADADDR(
- create_var(UK_LIBPARAM_PREFIX,__param_arg));
-
- create_var(UK_LIBPARAM_PREFIX,__param_arg) : {
- KEEP (*(create_var(UK_LIBPARAM_PREFIX,PARAM_SECTION_SUFFIX)))
+SECTIONS {
+ . = ALIGN(8);
+ .UK_LIBPARAM_PARAMSECTION_NAME : {
+ PROVIDE(UK_LIBPARAM_PARAMSECTION_STARTSYM = .);
+ KEEP(*(.UK_LIBPARAM_PARAMSECTION_NAME))
+ KEEP(*(.UK_LIBPARAM_PARAMSECTION_NAME.*))
+ PROVIDE(UK_LIBPARAM_PARAMSECTION_ENDSYM = .);
}
- /**
- * creates a variable name = LOADADDR(section_name);
- */
- _SECTION_STOP(
- _LIB_PARAM_SECTION_NAME(UK_LIBPARAM_PREFIX,
- PARAM_SECTION_SUFFIX)
- ) = LOADADDR(
- create_var(UK_LIBPARAM_PREFIX,__param_arg)) +
- SIZEOF(
- create_var(UK_LIBPARAM_PREFIX,__param_arg));
}
INSERT AFTER .rodata
-#endif /* UK_LIBPARAM_PREFIX */
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * Authors: Sharan Santhanam <sharan.santhanam@neclab.eu>
- *
- * 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.
- */
-#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 ARRAY_SEP ' '
-#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)
-
-#define PARGS_PARAM_SET(pargs, parameter, len) \
- do { \
- if ((pargs)->param_len) \
- uk_pr_warn("Found no value. Parameter %s skipped\n",\
- (pargs)->param); \
- (pargs)->param = (parameter); \
- (pargs)->param_len = (len); \
- } 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 const char *str_param_type(const struct uk_param *param);
-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 const char *str_param_type(const struct uk_param *param)
-{
- const char *ret;
-
- UK_ASSERT(param);
-
- switch (param->param_type) {
- case _LIB_PARAM___s8: /* _LIB_PARAM_char */
- ret = "char";
- break;
- case _LIB_PARAM___u8:
- ret = "u8";
- break;
- case _LIB_PARAM___s16:
- ret = "s16";
- break;
- case _LIB_PARAM___u16:
- ret = "u16";
- break;
- case _LIB_PARAM___s32: /* _LIB_PARAM_int */
- ret = "int";
- break;
- case _LIB_PARAM___u32:
- ret = "uint";
- break;
- case _LIB_PARAM___s64:
- ret = "s64";
- break;
- case _LIB_PARAM___u64:
- ret = "u64";
- break;
- case _LIB_PARAM___uptr: /* _LIB_PARAM_charp */
- ret = "string";
- break;
- default:
- ret = "?";
- break;
- }
-
- return ret;
-}
-
-static void uk_usage(const char *progname)
-{
- struct uk_lib_section *section;
- struct uk_param *param;
- int i, j, len, type_size;
-
- printf("Usage: %s\n", progname);
- printf(" [[UNIKRAFT OPTION]].. [[UNIKRAFT LIBRARY ARGUMENT]].. -- [[APPLICATION ARGUMENT]]..\n\n");
- printf("Unikraft options:\n");
- printf(" -h, --help display this help and exit\n");
- printf(" -V, --version display Unikraft version and exit\n\n");
- printf("Unikraft library arguments:\n");
- uk_list_for_each_entry(section, &uk_libsections, next) {
- len = section->len / sizeof(struct uk_param);
- param = section->sec_addr_start;
-
- UK_ASSERT(param);
- UK_ASSERT(param->param_type > 0);
- UK_ASSERT(param->param_size > 0);
-
- for (i = 0; i < len; i++, param++) {
- type_size = (param->param_type >> PARAM_SIZE_SHIFT)
- & PARAM_SIZE_MASK;
-
- UK_ASSERT(type_size > 0);
-
- printf(" %s=[%s]",
- param->name, str_param_type(param));
-
- /* ...in case we have an array: */
- for (j = param->param_size / type_size;
- j > 1;
- --j) {
- printf("%c[%s]",
- ARRAY_SEP, str_param_type(param));
- }
-
- printf("\n");
- }
- }
- printf("\n");
- printf("For application arguments refer to the application manual or application help.\n");
- printf("For example, use `-h` as application argument.\n");
- fflush(stdout);
-}
-
-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;
- 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++;
- 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_SET(pargs, args[i], 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_SET(pargs, args[i],
- 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_SET(pargs, args[i], len);
- 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 i = 0;
- char *start, *value;
- 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) {
- /* Adding support for array */
- i = 0;
- value = &pargs->value[i];
- uk_pr_debug("Value:%s length: %d\n", value,
- pargs->value_len);
- while (value && i < param->param_size) {
- start = value;
- value = strchr(value, ARRAY_SEP);
- if (value) {
- uk_pr_debug("Delimiter: %p\n", value);
- *value = '\0';
- /* Search from the next index */
- value++;
- }
- uk_pr_debug("Array index: %d contains %s\n",
- i, start);
- rc = kernel_arg_set((void *)(param->addr +
- (i * param_type)),
- start, param_type, sign);
- if (rc < 0)
- break;
- i++;
- }
- if (rc < 0)
- uk_pr_err("Failed to read element at index: %d\n",
- i);
- else if (value && i == param->param_size)
- uk_pr_warn("Overflow detected! Max array size:%d\n",
- param->param_size);
- else
- uk_pr_debug("Converted value: %s into an array containing %d elements\n",
- pargs->value, i);
- } 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, i;
- 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));
- uk_pr_err("Skipping Args:");
- for ( i = cnt; i < cnt + args_read; i++)
- uk_pr_err(" %s", argv[i]);
- uk_pr_err("\n");
- 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, §ion);
- 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, ¶m);
- 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);
- if (rc < 0) {
- uk_pr_err("Failed to set %s param\n", pargs.param);
- continue;
- }
-
- uk_pr_info("Parsed %d args\n", cnt);
- }
-
- /* Replacing the -- with progname */
- argv[keindex] = DECONST(char *, progname);
-
- return keindex + 1;
-}
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Authors: Sharan Santhanam <sharan.santhanam@neclab.eu>
+ * Simon Kuenzer <simon@unikraft.io>
+ *
+ * Copyright (c) 2019, NEC Europe Ltd., NEC Corporation. All rights reserved.
+ * Copyright (c) 2023, Unikraft GmbH. 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.
+ */
+#include <uk/config.h>
+#include <uk/libparam.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <uk/list.h>
+#include <uk/arch/limits.h>
+#include <uk/print.h>
+#include <uk/assert.h>
+#include <uk/version.h>
+#include <uk/essentials.h>
+
+static UK_LIST_HEAD(ld_head);
+
+#define PARSE_PARAM_SEP '.'
+#define PARSE_VALUE_SEP '='
+#define PARSE_LIST_START '['
+#define PARSE_LIST_END ']'
+#define PARSE_STOP "--"
+#define PARSE_USAGE "help"
+
+void _uk_libparam_libsec_register(struct uk_libparam_libdesc *ld)
+{
+ uk_list_add_tail(&ld->next, &ld_head);
+}
+
+static const char *str_param_type(enum uk_libparam_param_type pt)
+{
+ switch (pt) {
+ case _UK_LIBPARAM_PARAM_TYPE_bool:
+ return "bool";
+ case _UK_LIBPARAM_PARAM_TYPE___s8:
+ return "s8";
+ case _UK_LIBPARAM_PARAM_TYPE___u8:
+ return "u8";
+ case _UK_LIBPARAM_PARAM_TYPE___s16:
+ return "s16";
+ case _UK_LIBPARAM_PARAM_TYPE___u16:
+ return "u16";
+ case _UK_LIBPARAM_PARAM_TYPE___s32:
+ return "s32";
+ case _UK_LIBPARAM_PARAM_TYPE___u32:
+ return "u32";
+ case _UK_LIBPARAM_PARAM_TYPE___s64:
+ return "s64";
+ case _UK_LIBPARAM_PARAM_TYPE___u64:
+ return "u64";
+ case _UK_LIBPARAM_PARAM_TYPE___uptr:
+ return "uptr";
+ case _UK_LIBPARAM_PARAM_TYPE_charp:
+ return "charp";
+ default:
+ break;
+ }
+ return "<?>";
+}
+
+#define UK_LIBPARAM_FOREACH_LIBDESC(ld_iter) \
+ uk_list_for_each_entry((ld_iter), &ld_head, next)
+#define UK_LIBPARAM_FOREACH_PARAMIDX(ld, p_iter) \
+ for ((p_iter) = 0; (p_iter) < (ld)->params_len; (p_iter)++)
+#define UK_LIBPARAM_PARAM_GET(libdesc, idx) \
+ (((idx) >= (libdesc)->params_len) ? __NULL : ((libdesc)->params)[idx])
+
+static void uk_usage(void)
+{
+ struct uk_libparam_libdesc *ld;
+ struct uk_libparam_param *p;
+ __sz p_i;
+
+ /*
+ * FIXME: Use a console print variant without context prefix
+ * (Update as soon as it is available)
+ */
+ uk_pr_info("Usage of command line:\n"
+ " [%s] [PREFIX%cPARAMETER%cVALUE]... %s [APPLICATION ARGUMENT]...\n\n",
+ PARSE_USAGE, PARSE_PARAM_SEP, PARSE_VALUE_SEP, PARSE_STOP);
+ uk_pr_info("Special commands:\n"
+ "%12s Print this help summary\n\n",
+ PARSE_USAGE);
+ uk_pr_info("Available parameters:\n");
+ UK_LIBPARAM_FOREACH_LIBDESC(ld) {
+ UK_LIBPARAM_FOREACH_PARAMIDX(ld, p_i) {
+ p = UK_LIBPARAM_PARAM_GET(ld, p_i);
+ UK_ASSERT(p);
+
+ if (p->count == 0)
+ continue;
+ uk_pr_info("%12s.%-18s%s (",
+ ld->prefix, p->name,
+ p->desc ? p->desc : p->name);
+ if (p->count > 1)
+ uk_pr_info("array[%"__PRIsz"] of ",
+ p->count);
+ uk_pr_info("%s)\n", str_param_type(p->type));
+ }
+ }
+ uk_pr_info("\n"
+ "Numbers can be passed in decimal, octal (\"0\" as prefix), or hexadecimal (\"0x\" as prefix).\n");
+ uk_pr_info("Valid boolean values for 'true' are: \"true\", \"on\", \"yes\", a non-zero number.\n"
+ "Valid boolean values for 'false' are: \"false\", \"off\", \"no\", a zero number (e.g., \"0\").\n");
+ uk_pr_info("Boolean parameters that are passed without a value will be set to 'true'.\n");
+ uk_pr_info("Array parameters can be passed with multiple 'LIBRARY%cPARAMETER%cVALUE' tokens,\n",
+ PARSE_PARAM_SEP, PARSE_VALUE_SEP);
+ uk_pr_info("using a list: 'PREFIX%cPARAMETER%c%c VALUE0 VALUE1 ... %c', or a combination of both.\n",
+ PARSE_PARAM_SEP, PARSE_VALUE_SEP, PARSE_LIST_START,
+ PARSE_LIST_END);
+ uk_pr_info("Please refer the application manual or application help for application arguments.\n");
+}
+
+enum parse_arg_state {
+ PAS_PARAM, /* Parsing parameters and single value assignments */
+ PAS_LIST, /* Parsing list of values */
+};
+
+/* Parser context/state */
+struct parse_arg_ctx {
+ int hit_stop;
+ int hit_usage;
+ enum parse_arg_state state;
+
+ struct uk_libparam_libdesc *ld;
+ struct uk_libparam_param *p;
+};
+
+static struct uk_libparam_libdesc *find_libdesc(const char *libname,
+ __sz libname_len)
+{
+ struct uk_libparam_libdesc *ld_iter;
+
+ UK_LIBPARAM_FOREACH_LIBDESC(ld_iter) {
+ /*
+ * We want an exact match, because libname might not
+ * be zero terminated, we need to compare library name and the
+ * lengths separately.
+ */
+ if ((strncmp(ld_iter->prefix, libname, libname_len) == 0)
+ && (ld_iter->prefix[libname_len] == '\0'))
+ return ld_iter;
+ }
+ return NULL;
+}
+
+static struct uk_libparam_param *find_libparam(struct uk_libparam_libdesc *ld,
+ const char *paramname,
+ __sz paramname_len)
+{
+ struct uk_libparam_param *p;
+ __sz i;
+
+ UK_ASSERT(ld);
+
+ UK_LIBPARAM_FOREACH_PARAMIDX(ld, i) {
+ p = UK_LIBPARAM_PARAM_GET(ld, i);
+
+ /*
+ * We look for an exact match, because `paramname` might not
+ * be zero terminated. First, we need to compare strings and
+ * then their lengths. Because we know the length of `paramname`
+ * we can simply check if the position of the zero termination
+ * symbol matches.
+ */
+ if ((strncmp(p->name, paramname, paramname_len) == 0)
+ && (p->name[paramname_len] == '\0'))
+ return p;
+ }
+ return NULL;
+}
+
+/*
+ * Internal and stripped down version of strtoull that does not use `errno`.
+ * The parsed integer value is returned on `result` and its sign on
+ * `result_is_neg`. This function is derived from nolibc.
+ */
+static __ssz __safe_strntoull(const char *nptr, __sz maxlen, char **endptr,
+ int base, unsigned long long *result,
+ int *result_is_neg)
+{
+ const char *s = nptr;
+ unsigned long long acc = 0;
+ unsigned char c;
+ unsigned long long qbase, cutoff;
+ int neg, any, cutlim;
+ __sz len = 0;
+ int err = 0;
+
+ UK_ASSERT(nptr);
+ UK_ASSERT(result);
+ UK_ASSERT(result_is_neg);
+ UK_ASSERT((base >= 0) && (base <= 36));
+
+ if (maxlen == 0) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ c = *s++;
+ while (isspace(c) && len < maxlen) {
+ c = *s++;
+ len++;
+ }
+
+ /* Detect sign */
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ len++;
+ } else {
+ neg = 0;
+ if (c == '+') {
+ c = *s++;
+ len++;
+ }
+ }
+ /* Detect base */
+ if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ len += 2;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ qbase = (unsigned int)base;
+ cutoff = (unsigned long long) __ULL_MAX / qbase;
+ cutlim = (unsigned long long) __ULL_MAX % qbase;
+ for (acc = 0, any = 0; len < maxlen && c != '\0'; c = *s++, len++) {
+ if (!isascii(c)) {
+ err = -EINVAL;
+ break;
+ }
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else {
+ err = -EINVAL;
+ break;
+ }
+ if (c >= base) {
+ err = -EINVAL;
+ break;
+ }
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= qbase;
+ acc += c;
+ }
+ }
+ if (any < 0)
+ err = -ERANGE;
+
+ /* Trailing whitespaces */
+ while (isspace(c) && len < maxlen) {
+ c = *s++;
+ len++;
+ }
+
+exit:
+ if (endptr != 0)
+ *endptr = DECONST(char *, any ? s - 1 : nptr);
+ if (err < 0)
+ return err;
+
+ *result = acc;
+ *result_is_neg = neg;
+ return (__ssz) len;
+}
+
+#define do_write_value(p, type_val, widx, raw_val) \
+ do { \
+ uk_pr_debug("Writing '0x%"__PRIx64"' to '%s' " \
+ "(%s, %p[%"__PRIsz"]=%p)\n", \
+ (__s64) raw_value, (p)->name, \
+ str_param_type((p)->type), \
+ (p)->addr, widx, \
+ &(((type_val *) (p)->addr)[widx])); \
+ ((type_val *) (p)->addr)[widx] = (type_val) (raw_val); \
+ } while (0)
+
+/* WARNING: Since we do not have a __maxint type, we take __uptr as generic
+ * value container for all int types and charp pointer
+ */
+static int write_value(struct uk_libparam_param *p, __uptr raw_value)
+{
+ UK_ASSERT(p);
+ __sz widx = 0;
+
+ if (p->count == 0) {
+ /* If count is equal to 0, we never have space */
+ return -ENOSPC;
+ } else if (p->count == 1) {
+ /* Always overwrite single value parameters */
+ widx = 0;
+ } else {
+ /* Take next index within array bounds,
+ * otherwise return -ENOSPC
+ */
+ if (p->__widx >= p->count)
+ return -ENOSPC;
+ widx = p->__widx++;
+ }
+
+ switch (p->type) {
+ case _UK_LIBPARAM_PARAM_TYPE_bool:
+ if (raw_value != 0)
+ raw_value = 1; /* Normalization */
+ do_write_value(p, int, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s8:
+ do_write_value(p, __s8, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u8:
+ do_write_value(p, __u8, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s16:
+ do_write_value(p, __s16, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u16:
+ do_write_value(p, __u16, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s32:
+ do_write_value(p, __s32, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u32:
+ do_write_value(p, __u32, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s64:
+ do_write_value(p, __s64, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u64:
+ do_write_value(p, __u64, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___uptr:
+ do_write_value(p, __uptr, widx, raw_value);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE_charp:
+ do_write_value(p, __uptr, widx, raw_value);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int parse_value(struct parse_arg_ctx *ctx, char *value, __sz value_len)
+{
+ int is_negative;
+ unsigned long long num;
+ __ssz len;
+
+ UK_ASSERT(ctx);
+ UK_ASSERT(ctx->ld);
+ UK_ASSERT(ctx->p);
+
+ /* Handle special string cases for boolean and charp */
+ switch (ctx->p->type) {
+ case _UK_LIBPARAM_PARAM_TYPE_bool:
+ if ((!value) ||
+ (value_len >= 2 &&
+ strncmp("on", value, value_len) == 0) ||
+ (value_len >= 3 &&
+ strncmp("yes", value, value_len) == 0) ||
+ (value_len >= 4 &&
+ strncmp("true", value, value_len) == 0)) {
+ return write_value(ctx->p, 1); /* Write "true" */
+ } else if ((value_len >= 3 &&
+ strncmp("off", value, value_len) == 0) ||
+ (value_len >= 2 &&
+ strncmp("no", value, value_len) == 0) ||
+ (value_len >= 5 &&
+ strncmp("false", value, value_len) == 0)) {
+ return write_value(ctx->p, 0); /* Write "false" */
+ }
+ break; /* Continue treating value as a number */
+
+ case _UK_LIBPARAM_PARAM_TYPE_charp:
+ if (!value) {
+ uk_pr_warn("No value given to %s.%s (charp)\n",
+ ctx->ld->prefix, ctx->p->name);
+ return -EINVAL;
+ }
+
+ /* Ensure '\0'-termination and store reference to
+ * parameter variable
+ */
+ value[value_len] = '\0';
+ return write_value(ctx->p, (__uptr) value);
+
+ default:
+ break;
+ }
+
+ /*
+ * Convert string to number
+ * NOTE: We should never enter here with _UK_LIBPARAM_PARAM_TYPE_charp,
+ * _UK_LIBPARAM_PARAM_TYPE_bool may arrive here if we need to
+ * parse a number value for it.
+ */
+ UK_ASSERT(ctx->p->type != _UK_LIBPARAM_PARAM_TYPE_charp);
+
+ /* From here on, we cannot continue without having a value to convert */
+ if (!value || (value_len == 0))
+ goto novalue;
+
+ /* Parse `value` and store result in `num` */
+ len = __safe_strntoull(value, value_len, __NULL,
+ 0 /* auto-detect base */,
+ &num, &is_negative);
+ if ((len < 0) || ((__sz) len < value_len)) {
+ if (len == -ERANGE)
+ goto outofrange;
+
+ uk_pr_debug("len:%"__PRIssz" value_len:%"__PRIsz"\n",
+ len, value_len);
+ goto malformed;
+ }
+
+ /* Check if resulting number fits */
+#define do_check_num(num, is_negative, min, max) \
+ do { \
+ if (num > (unsigned long long) max) \
+ goto outofrange; \
+ if (is_negative) { \
+ num = -num; \
+ if ((long long) num < (long long) min) \
+ goto outofrange; \
+ } \
+ } while (0)
+
+ switch (ctx->p->type) {
+ case _UK_LIBPARAM_PARAM_TYPE___s8:
+ do_check_num(num, is_negative, __S8_MIN, __S8_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s16:
+ do_check_num(num, is_negative, __S16_MIN, __S16_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s32:
+ do_check_num(num, is_negative, __S32_MIN, __S32_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___s64:
+ do_check_num(num, is_negative, __S64_MIN, __S64_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u8:
+ do_check_num(num, is_negative, 0, __U8_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u16:
+ do_check_num(num, is_negative, 0, __U16_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u32:
+ do_check_num(num, is_negative, 0, __U32_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___u64:
+ do_check_num(num, is_negative, 0, __U64_MAX);
+ break;
+ case _UK_LIBPARAM_PARAM_TYPE___uptr:
+ do_check_num(num, is_negative, 0, __PTR_MAX);
+ break;
+ default:
+ /* No checks or modification needed for bool */
+ break;
+ }
+#undef do_check_num
+
+ /* Write number to target parameter */
+ return write_value(ctx->p, (__uptr) num);
+
+novalue:
+ uk_pr_err("Parameter %s.%s requires a %s value\n",
+ ctx->ld->prefix, ctx->p->name, str_param_type(ctx->p->type));
+ return -EINVAL;
+
+outofrange:
+ uk_pr_err("Parameter %s.%s (%s): Given number is out of range\n",
+ ctx->ld->prefix, ctx->p->name, str_param_type(ctx->p->type));
+ return -ERANGE;
+
+malformed:
+ uk_pr_err("Parameter %s.%s (%s): Given number is malformed\n",
+ ctx->ld->prefix, ctx->p->name, str_param_type(ctx->p->type));
+ return -EINVAL;
+}
+
+static int parse_arg(struct parse_arg_ctx *ctx, char *strbuf, int scan_only)
+{
+ UK_ASSERT(ctx);
+ UK_ASSERT(strbuf);
+
+ char *value;
+ __sz value_len;
+ int ret = 0;
+
+ if (strbuf[0] == '\0') {
+ /* Empty string: We skip this snippet */
+ return 0;
+ }
+
+ uk_pr_debug("Parsing snippet: \"%s\"\n", strbuf);
+ switch (ctx->state) {
+ case PAS_PARAM:
+ /*
+ * Catch stop sequence ('---') or usage command ('help')
+ */
+ if (strcmp(strbuf, PARSE_STOP) == 0) {
+ ctx->hit_stop = 1;
+ return 0;
+ }
+ if (strcmp(strbuf, PARSE_USAGE) == 0) {
+ ctx->hit_usage = 1;
+ return 0;
+ }
+
+ /* At this stage we parse the library and parameter name.
+ * We expect the input to be in the following form:
+ * libname.parameter=value
+ */
+ do {
+ char *libname = strbuf;
+ __sz libname_len;
+ char *paramname;
+ __sz paramname_len;
+
+ paramname = strchr(libname, PARSE_PARAM_SEP);
+ if (!paramname) {
+ uk_pr_debug(" Failed to determine library and parameter names (separator '%c' not found)\n",
+ PARSE_PARAM_SEP);
+ return -EINVAL;
+ }
+ libname_len = (__sz)((__uptr) paramname -
+ (__uptr) libname);
+ paramname += 1; /* skip leading char (= separator) */
+
+ /*
+ * Check if a value was given (by looking for '='
+ * separator). Note, a value is optional; it sets
+ * boolean values to TRUE
+ */
+ value = strchr(paramname, PARSE_VALUE_SEP);
+ if (!value) {
+ uk_pr_debug(" No value given (separator '%c' not found), trying without...\n",
+ PARSE_VALUE_SEP);
+ paramname_len = strlen(paramname);
+ value_len = 0;
+ } else {
+ paramname_len = (__sz)((__uptr) value -
+ (__uptr) paramname);
+ value += 1; /* skip leading char (separator) */
+ value_len = strlen(value);
+ }
+ uk_pr_debug(" Parsed token:\n");
+ uk_pr_debug(" libprefix: \"%.*s\"\n",
+ (int) libname_len, libname);
+ uk_pr_debug(" paramname: \"%.*s\"\n",
+ (int) paramname_len, paramname);
+
+ /* Search database for corresponding libname.paramname
+ * entry.
+ * NOTE: Because we accept only exact matches, this
+ * automatically filters wrongly detected libname
+ * and paramname pairs (e.g., having whitespaces,
+ * not allowed characters, or empty strings).
+ */
+ if (!scan_only) {
+ ctx->ld = find_libdesc(libname, libname_len);
+ if (ctx->ld)
+ ctx->p = find_libparam(ctx->ld,
+ paramname,
+ paramname_len);
+ if (!ctx->ld || !ctx->p) {
+ uk_pr_warn("Parameter %.*s.%.*s: Unknown or invalid\n",
+ (int) libname_len, libname,
+ (int) paramname_len, paramname);
+ ret = -ENOENT;
+ ctx->p = NULL;
+ }
+ }
+
+ /* Do we have a value list? (first char must be '[') */
+ if (value && value[0] == PARSE_LIST_START) {
+ ctx->state = PAS_LIST;
+ value += 1; /* skip list start character */
+ value_len -= 1;
+
+ if (value_len > 0)
+ goto parse_list;
+ break;
+ }
+
+ /* Parse value (ctx->param points to current value) */
+ uk_pr_debug(" value: \"%s\"\n",
+ value ? value : "<NULL>");
+ if (ctx->p && !scan_only)
+ ret = parse_value(ctx, value, value_len);
+ } while (0);
+ break;
+
+ case PAS_LIST:
+ value = strbuf;
+ value_len = strlen(value);
+parse_list:
+ /* Parse values until the end of the values list.
+ * For the end, we only need to check if we have a terminating
+ * ']' character.
+ */
+ if ((value_len >= 1)
+ && (value[value_len - 1] == PARSE_LIST_END)) {
+ /* Because we reached the end of the list,
+ * we will stop processing the list
+ */
+ ctx->state = PAS_PARAM;
+ value_len -= 1; /* Hide terminating char */
+
+ if (value_len == 0) {
+ /* We had an end of list character, only */
+ ctx->state = PAS_PARAM;
+ break;
+ }
+ }
+
+ uk_pr_debug(" list value: \"%.*s\"\n", (int) value_len, value);
+ if (ctx->p && !scan_only)
+ ret = parse_value(ctx, value, value_len);
+ break;
+ }
+
+ return ret;
+}
+
+static inline void __reset_p_widx(void)
+{
+ struct uk_libparam_libdesc *ld;
+ struct uk_libparam_param *p;
+ __sz p_i;
+
+ UK_LIBPARAM_FOREACH_LIBDESC(ld) {
+ UK_LIBPARAM_FOREACH_PARAMIDX(ld, p_i) {
+ p = UK_LIBPARAM_PARAM_GET(ld, p_i);
+ UK_ASSERT(p);
+ p->__widx = 0;
+ }
+ }
+}
+
+int uk_libparam_parse(int argc, char **argv, int flags)
+{
+ struct parse_arg_ctx c;
+ int rc;
+ int i;
+
+ memset(&c, 0x0, sizeof(c));
+ /* Reset __widx fields */
+ __reset_p_widx();
+
+ /* Feed arguments from vector to parse_arg() */
+ for (i = 0; i < argc; ++i) {
+ rc = parse_arg(&c, argv[i], (flags & UK_LIBPARAM_F_SCAN));
+ if (rc < 0 && (flags & UK_LIBPARAM_F_STRICT))
+ return rc;
+
+ if (c.hit_stop) {
+ /* Stop sequence detected */
+ break;
+ }
+ }
+
+ if (c.hit_usage && (flags & UK_LIBPARAM_F_USAGE)) {
+ uk_usage();
+ return -EINTR;
+ }
+ return i;
+}