Allow the active timer (providing udelay() and currticks()) to be selected at runtime based on probing during the INIT_EARLY stage of initialisation. TICKS_PER_SEC is now a fixed compile-time constant for all builds, and is independent of the underlying clock tick rate. We choose the value 1024 to allow multiplications and divisions on seconds to be converted to bit shifts. TICKS_PER_MS is defined as 1, allowing multiplications and divisions on milliseconds to be omitted entirely. The 2% inaccuracy in this definition is negligible when using the standard BIOS timer (running at around 18.2Hz). TIMER_RDTSC now checks for a constant TSC before claiming to be a usable timer. (This timer can be tested in KVM via the command-line option "-cpu host,+invtsc".) Signed-off-by: Michael Brown <mcb30@ipxe.org>
455 lines
11 KiB
C
455 lines
11 KiB
C
/*
|
|
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
* You can also choose to distribute this program under the terms of
|
|
* the Unmodified Binary Distribution Licence (as given in the file
|
|
* COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <ipxe/netdevice.h>
|
|
#include <ipxe/menu.h>
|
|
#include <ipxe/settings.h>
|
|
#include <ipxe/params.h>
|
|
#include <ipxe/timer.h>
|
|
#include <ipxe/parseopt.h>
|
|
#include <config/branding.h>
|
|
|
|
/** @file
|
|
*
|
|
* Command line option parsing
|
|
*
|
|
*/
|
|
|
|
/** Return status code for "--help" option */
|
|
#define ECANCELED_NO_OP __einfo_error ( EINFO_ECANCELED_NO_OP )
|
|
#define EINFO_ECANCELED_NO_OP \
|
|
__einfo_uniqify ( EINFO_ECANCELED, 0x01, "Nothing to do" )
|
|
|
|
/* Disambiguate the various error codes */
|
|
#define EINVAL_INTEGER __einfo_error ( EINFO_EINVAL_INTEGER )
|
|
#define EINFO_EINVAL_INTEGER \
|
|
__einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid integer value" )
|
|
#define EINVAL_UNKNOWN_OPTION __einfo_error ( EINFO_EINVAL_UNKNOWN_OPTION )
|
|
#define EINFO_EINVAL_UNKNOWN_OPTION \
|
|
__einfo_uniqify ( EINFO_EINVAL, 0x02, "Unrecognised option" )
|
|
#define EINVAL_MISSING_ARGUMENT __einfo_error ( EINFO_EINVAL_MISSING_ARGUMENT )
|
|
#define EINFO_EINVAL_MISSING_ARGUMENT \
|
|
__einfo_uniqify ( EINFO_EINVAL, 0x03, "Missing argument" )
|
|
|
|
/**
|
|
* Parse string value
|
|
*
|
|
* @v text Text
|
|
* @ret value String value
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_string ( char *text, char **value ) {
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Parse string */
|
|
*value = text;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse integer value
|
|
*
|
|
* @v text Text
|
|
* @ret value Integer value
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_integer ( char *text, unsigned int *value ) {
|
|
char *endp;
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Parse integer */
|
|
*value = strtoul ( text, &endp, 0 );
|
|
if ( *endp ) {
|
|
printf ( "\"%s\": invalid integer value\n", text );
|
|
return -EINVAL_INTEGER;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse timeout value (in ms)
|
|
*
|
|
* @v text Text
|
|
* @ret value Integer value
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_timeout ( char *text, unsigned long *value ) {
|
|
unsigned int value_ms;
|
|
int rc;
|
|
|
|
/* Parse raw integer value */
|
|
if ( ( rc = parse_integer ( text, &value_ms ) ) != 0 )
|
|
return rc;
|
|
|
|
/* Convert to a number of timer ticks */
|
|
*value = ( value_ms * TICKS_PER_MS );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse network device name
|
|
*
|
|
* @v text Text
|
|
* @ret netdev Network device
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_netdev ( char *text, struct net_device **netdev ) {
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Find network device */
|
|
*netdev = find_netdev ( text );
|
|
if ( ! *netdev ) {
|
|
printf ( "\"%s\": no such network device\n", text );
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse network device configurator name
|
|
*
|
|
* @v text Text
|
|
* @ret configurator Network device configurator
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_netdev_configurator ( char *text,
|
|
struct net_device_configurator **configurator ){
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Find network device configurator */
|
|
*configurator = find_netdev_configurator ( text );
|
|
if ( ! *configurator ) {
|
|
printf ( "\"%s\": no such configurator\n", text );
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse menu name
|
|
*
|
|
* @v text Text
|
|
* @ret menu Menu
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_menu ( char *text, struct menu **menu ) {
|
|
|
|
/* Find menu */
|
|
*menu = find_menu ( text );
|
|
if ( ! *menu ) {
|
|
if ( text ) {
|
|
printf ( "\"%s\": no such menu\n", text );
|
|
} else {
|
|
printf ( "No default menu\n" );
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse flag
|
|
*
|
|
* @v text Text (ignored)
|
|
* @ret flag Flag to set
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_flag ( char *text __unused, int *flag ) {
|
|
|
|
/* Set flag */
|
|
*flag = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse key
|
|
*
|
|
* @v text Text
|
|
* @ret key Key
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_key ( char *text, unsigned int *key ) {
|
|
|
|
/* Interpret single characters as being a literal key character */
|
|
if ( text[0] && ! text[1] ) {
|
|
*key = text[0];
|
|
return 0;
|
|
}
|
|
|
|
/* Otherwise, interpret as an integer */
|
|
return parse_integer ( text, key );
|
|
}
|
|
|
|
/**
|
|
* Parse settings block name
|
|
*
|
|
* @v text Text
|
|
* @ret value Integer value
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_settings ( char *text, struct settings **value ) {
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Parse scope name */
|
|
*value = find_settings ( text );
|
|
if ( ! *value ) {
|
|
printf ( "\"%s\": no such scope\n", text );
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse setting name
|
|
*
|
|
* @v text Text
|
|
* @v setting Named setting to fill in
|
|
* @v get_child Function to find or create child settings block
|
|
* @ret rc Return status code
|
|
*
|
|
* Note that this function modifies the original @c text.
|
|
*/
|
|
int parse_setting ( char *text, struct named_setting *setting,
|
|
get_child_settings_t get_child ) {
|
|
int rc;
|
|
|
|
/* Sanity check */
|
|
assert ( text != NULL );
|
|
|
|
/* Parse setting name */
|
|
if ( ( rc = parse_setting_name ( text, get_child, &setting->settings,
|
|
&setting->setting ) ) != 0 ) {
|
|
printf ( "\"%s\": invalid setting\n", text );
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse existing setting name
|
|
*
|
|
* @v text Text
|
|
* @v setting Named setting to fill in
|
|
* @ret rc Return status code
|
|
*
|
|
* Note that this function modifies the original @c text.
|
|
*/
|
|
int parse_existing_setting ( char *text, struct named_setting *setting ) {
|
|
|
|
return parse_setting ( text, setting, find_child_settings );
|
|
}
|
|
|
|
/**
|
|
* Parse and autovivify setting name
|
|
*
|
|
* @v text Text
|
|
* @v setting Named setting to fill in
|
|
* @ret rc Return status code
|
|
*
|
|
* Note that this function modifies the original @c text.
|
|
*/
|
|
int parse_autovivified_setting ( char *text, struct named_setting *setting ) {
|
|
|
|
return parse_setting ( text, setting, autovivify_child_settings );
|
|
}
|
|
|
|
/**
|
|
* Parse form parameter list name
|
|
*
|
|
* @v text Text
|
|
* @ret params Parameter list
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_parameters ( char *text, struct parameters **params ) {
|
|
|
|
/* Find parameter list */
|
|
*params = find_parameters ( text );
|
|
if ( ! *params ) {
|
|
if ( text ) {
|
|
printf ( "\"%s\": no such parameter list\n", text );
|
|
} else {
|
|
printf ( "No default parameter list\n" );
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Print command usage message
|
|
*
|
|
* @v cmd Command descriptor
|
|
* @v argv Argument list
|
|
*/
|
|
void print_usage ( struct command_descriptor *cmd, char **argv ) {
|
|
struct option_descriptor *option;
|
|
unsigned int i;
|
|
int is_optional;
|
|
|
|
printf ( "Usage:\n\n %s", argv[0] );
|
|
for ( i = 0 ; i < cmd->num_options ; i++ ) {
|
|
option = &cmd->options[i];
|
|
printf ( " [-%c|--%s", option->shortopt, option->longopt );
|
|
if ( option->has_arg ) {
|
|
is_optional = ( option->has_arg == optional_argument );
|
|
printf ( " %s<%s>%s", ( is_optional ? "[" : "" ),
|
|
option->longopt, ( is_optional ? "]" : "" ) );
|
|
}
|
|
printf ( "]" );
|
|
}
|
|
if ( cmd->usage )
|
|
printf ( " %s", cmd->usage );
|
|
printf ( "\n\nSee " PRODUCT_COMMAND_URI " for further information\n",
|
|
argv[0] );
|
|
}
|
|
|
|
/**
|
|
* Reparse command-line options
|
|
*
|
|
* @v argc Argument count
|
|
* @v argv Argument list
|
|
* @v cmd Command descriptor
|
|
* @v opts Options (already initialised with default values)
|
|
* @ret rc Return status code
|
|
*/
|
|
int reparse_options ( int argc, char **argv, struct command_descriptor *cmd,
|
|
void *opts ) {
|
|
struct option longopts[ cmd->num_options + 1 /* help */ + 1 /* end */ ];
|
|
char shortopts[ cmd->num_options * 3 /* possible "::" */ + 1 /* "h" */
|
|
+ 1 /* NUL */ ];
|
|
unsigned int shortopt_idx = 0;
|
|
int ( * parse ) ( char *text, void *value );
|
|
void *value;
|
|
unsigned int i;
|
|
unsigned int j;
|
|
unsigned int num_args;
|
|
int c;
|
|
int rc;
|
|
|
|
/* Construct long and short option lists for getopt_long() */
|
|
memset ( longopts, 0, sizeof ( longopts ) );
|
|
for ( i = 0 ; i < cmd->num_options ; i++ ) {
|
|
longopts[i].name = cmd->options[i].longopt;
|
|
longopts[i].has_arg = cmd->options[i].has_arg;
|
|
longopts[i].val = cmd->options[i].shortopt;
|
|
shortopts[shortopt_idx++] = cmd->options[i].shortopt;
|
|
assert ( cmd->options[i].has_arg <= optional_argument );
|
|
for ( j = cmd->options[i].has_arg ; j > 0 ; j-- )
|
|
shortopts[shortopt_idx++] = ':';
|
|
}
|
|
longopts[i].name = "help";
|
|
longopts[i].val = 'h';
|
|
shortopts[shortopt_idx++] = 'h';
|
|
shortopts[shortopt_idx++] = '\0';
|
|
assert ( shortopt_idx <= sizeof ( shortopts ) );
|
|
DBGC ( cmd, "Command \"%s\" has options \"%s\", %d-%d args, len %d\n",
|
|
argv[0], shortopts, cmd->min_args, cmd->max_args, cmd->len );
|
|
|
|
/* Parse options */
|
|
while ( ( c = getopt_long ( argc, argv, shortopts, longopts,
|
|
NULL ) ) >= 0 ) {
|
|
switch ( c ) {
|
|
case 'h' :
|
|
/* Print help */
|
|
print_usage ( cmd, argv );
|
|
return -ECANCELED_NO_OP;
|
|
case '?' :
|
|
/* Print usage message */
|
|
print_usage ( cmd, argv );
|
|
return -EINVAL_UNKNOWN_OPTION;
|
|
case ':' :
|
|
/* Print usage message */
|
|
print_usage ( cmd, argv );
|
|
return -EINVAL_MISSING_ARGUMENT;
|
|
default:
|
|
/* Search for an option to parse */
|
|
for ( i = 0 ; i < cmd->num_options ; i++ ) {
|
|
if ( c != cmd->options[i].shortopt )
|
|
continue;
|
|
parse = cmd->options[i].parse;
|
|
value = ( opts + cmd->options[i].offset );
|
|
if ( ( rc = parse ( optarg, value ) ) != 0 )
|
|
return rc;
|
|
break;
|
|
}
|
|
assert ( i < cmd->num_options );
|
|
}
|
|
}
|
|
|
|
/* Check remaining arguments */
|
|
num_args = ( argc - optind );
|
|
if ( ( num_args < cmd->min_args ) || ( num_args > cmd->max_args ) ) {
|
|
print_usage ( cmd, argv );
|
|
return -ERANGE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Parse command-line options
|
|
*
|
|
* @v argc Argument count
|
|
* @v argv Argument list
|
|
* @v cmd Command descriptor
|
|
* @v opts Options (may be uninitialised)
|
|
* @ret rc Return status code
|
|
*/
|
|
int parse_options ( int argc, char **argv, struct command_descriptor *cmd,
|
|
void *opts ) {
|
|
|
|
/* Clear options */
|
|
memset ( opts, 0, cmd->len );
|
|
|
|
return reparse_options ( argc, argv, cmd, opts );
|
|
}
|