From: Simon Kuenzer Date: Thu, 23 Feb 2023 23:42:29 +0000 (+0100) Subject: lib/uklibparam: Rewrite of parser X-Git-Tag: RELEASE-0.13.0~58 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=1cc3a2f6;p=unikraft%2Funikraft.git lib/uklibparam: Rewrite of parser This commit introduces a rewrite of `lib/uklibparam`. The rewrite ensures that it is errno-free, malloc-free, and TLS-free. This makes it ideal for processing library parameters early during boot. It includes an own and modified version of `strtoull` function that is used for parsing integer values from the command line. Additionally, the parser can handle arrays more intuitively (and for any data type of the elements) while fully utilizing pre-processed argument vectors. It relies on the fact that whitespacing and quoting is already handled (e.g., by `lib/ukargparse`). There are new types that are more inline with the ones defined by ukstore. This rewrite introduces the ability to provide a parameter description with updated parameter registration macros: UK_LIBPARAM_PARAM(variable, type, description) UK_LIBPARAM_PARAM_ARR(variable, type, count, description) The description is printed on the help output. The previous interfaces are kept for backwards compatibility. However, it is planned to remove them as soon as the code base is completely adopted. Checkpatch-Ignore: SPACING Checkpatch-Ignore: OPEN_BRACE Checkpatch-Ignore: MACRO_WITH_FLOW_CONTROL Signed-off-by: Simon Kuenzer Reviewed-by: Delia Pavel Approved-by: Razvan Deaconescu Tested-by: Unikraft CI GitHub-Closes: #868 --- diff --git a/lib/ukboot/boot.c b/lib/ukboot/boot.c index 12eda496c..01d74ad6b 100644 --- a/lib/ukboot/boot.c +++ b/lib/ukboot/boot.c @@ -227,7 +227,6 @@ void ukplat_entry_argp(char *arg0, char *argb, __sz argb_len) /* defined in */ void ukplat_entry(int argc, char *argv[]) { - int kern_args = 0; int rc = 0; #if CONFIG_LIBUKALLOC struct uk_alloc *a = NULL; @@ -251,12 +250,30 @@ void ukplat_entry(int argc, char *argv[]) } #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 */ @@ -306,9 +323,6 @@ void ukplat_entry(int argc, char *argv[]) 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(); diff --git a/lib/uklibparam/Makefile.rules b/lib/uklibparam/Makefile.rules index 3f7af4a27..0e8bb1c37 100644 --- a/lib/uklibparam/Makefile.rules +++ b/lib/uklibparam/Makefile.rules @@ -1,15 +1,21 @@ -# 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 diff --git a/lib/uklibparam/Makefile.uk b/lib/uklibparam/Makefile.uk index 3d450b861..46ed25ce6 100644 --- a/lib/uklibparam/Makefile.uk +++ b/lib/uklibparam/Makefile.uk @@ -4,4 +4,7 @@ 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 +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 diff --git a/lib/uklibparam/exportsyms.uk b/lib/uklibparam/exportsyms.uk index 94b6ca775..dcbb41d8b 100644 --- a/lib/uklibparam/exportsyms.uk +++ b/lib/uklibparam/exportsyms.uk @@ -1,2 +1,2 @@ uk_libparam_parse -_uk_libparam_lib_add +_uk_libparam_libsec_register diff --git a/lib/uklibparam/include/uk/libparam.h b/lib/uklibparam/include/uk/libparam.h index 0bf9fc320..c635cef61 100644 --- a/lib/uklibparam/include/uk/libparam.h +++ b/lib/uklibparam/include/uk/libparam.h @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: BSD-3-Clause */ /* * Authors: Sharan Santhanam + * Simon Kuenzer * * 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 @@ -45,407 +47,255 @@ 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. +/* 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 */ diff --git a/lib/uklibparam/libparam.lds.S b/lib/uklibparam/libparam.lds.S index bec697898..3ba2bb312 100644 --- a/lib/uklibparam/libparam.lds.S +++ b/lib/uklibparam/libparam.lds.S @@ -1,32 +1,12 @@ -#include #include -#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 */ diff --git a/lib/uklibparam/param.c b/lib/uklibparam/param.c deleted file mode 100644 index ae92bd597..000000000 --- a/lib/uklibparam/param.c +++ /dev/null @@ -1,655 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause */ -/* - * Authors: Sharan Santhanam - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/lib/uklibparam/parser.c b/lib/uklibparam/parser.c new file mode 100644 index 000000000..92bf9cf49 --- /dev/null +++ b/lib/uklibparam/parser.c @@ -0,0 +1,707 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Authors: Sharan Santhanam + * Simon Kuenzer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 : ""); + 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; +}