u-boot/common/cli.c
Andrew F. Davis 2dd468dbe1 dfu: Remove dependency on HUSH parser in SPL
CLI support with the HUSH parser is not currently SPL safe due to it's
use of realloc. That function is not defined for SPLs that use
SYS_MALLOC_SIMPLE. CLI support can be built in to SPL and some functions
do work, but use of some like run_command() will cause build to fail.
When no SPL code calls this function build works as the compiler removes
this unreachable code so the unresolved symbols are ignored.

If DFU support is enabled in SPL then MMU DFU support may get brought in
also, this code does make a call to run_command() causing build to fail
if the HUSH parser is not built-in. To break this odd and unneeded
dependency chain we use CONFIG_IS_ENABLED where appropriate to prevent
calls into HUSH code from SPL. This also removes our need to pull in the
rather unrelated source file when SPL_DFU is defined.

Signed-off-by: Andrew F. Davis <afd@ti.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
2019-01-26 08:13:55 -05:00

237 lines
5.4 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* Add to readline cmdline-editing by
* (C) Copyright 2005
* JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com>
*/
#include <common.h>
#include <cli.h>
#include <cli_hush.h>
#include <console.h>
#include <fdtdec.h>
#include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_CMDLINE
/*
* Run a command using the selected parser.
*
* @param cmd Command to run
* @param flag Execution flags (CMD_FLAG_...)
* @return 0 on success, or != 0 on error.
*/
int run_command(const char *cmd, int flag)
{
#if !CONFIG_IS_ENABLED(HUSH_PARSER)
/*
* cli_run_command can return 0 or 1 for success, so clean up
* its result.
*/
if (cli_simple_run_command(cmd, flag) == -1)
return 1;
return 0;
#else
int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
if (flag & CMD_FLAG_ENV)
hush_flags |= FLAG_CONT_ON_NEWLINE;
return parse_string_outer(cmd, hush_flags);
#endif
}
/*
* Run a command using the selected parser, and check if it is repeatable.
*
* @param cmd Command to run
* @param flag Execution flags (CMD_FLAG_...)
* @return 0 (not repeatable) or 1 (repeatable) on success, -1 on error.
*/
int run_command_repeatable(const char *cmd, int flag)
{
#ifndef CONFIG_HUSH_PARSER
return cli_simple_run_command(cmd, flag);
#else
/*
* parse_string_outer() returns 1 for failure, so clean up
* its result.
*/
if (parse_string_outer(cmd,
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
return -1;
return 0;
#endif
}
#endif /* CONFIG_CMDLINE */
int run_command_list(const char *cmd, int len, int flag)
{
int need_buff = 1;
char *buff = (char *)cmd; /* cast away const */
int rcode = 0;
if (len == -1) {
len = strlen(cmd);
#ifdef CONFIG_HUSH_PARSER
/* hush will never change our string */
need_buff = 0;
#else
/* the built-in parser will change our string if it sees \n */
need_buff = strchr(cmd, '\n') != NULL;
#endif
}
if (need_buff) {
buff = malloc(len + 1);
if (!buff)
return 1;
memcpy(buff, cmd, len);
buff[len] = '\0';
}
#ifdef CONFIG_HUSH_PARSER
rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
#else
/*
* This function will overwrite any \n it sees with a \0, which
* is why it can't work with a const char *. Here we are making
* using of internal knowledge of this function, to avoid always
* doing a malloc() which is actually required only in a case that
* is pretty rare.
*/
#ifdef CONFIG_CMDLINE
rcode = cli_simple_run_command_list(buff, flag);
#else
rcode = board_run_command(buff);
#endif
#endif
if (need_buff)
free(buff);
return rcode;
}
/****************************************************************************/
#if defined(CONFIG_CMD_RUN)
int do_run(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int i;
if (argc < 2)
return CMD_RET_USAGE;
for (i = 1; i < argc; ++i) {
char *arg;
arg = env_get(argv[i]);
if (arg == NULL) {
printf("## Error: \"%s\" not defined\n", argv[i]);
return 1;
}
if (run_command(arg, flag | CMD_FLAG_ENV) != 0)
return 1;
}
return 0;
}
#endif
#if CONFIG_IS_ENABLED(OF_CONTROL)
bool cli_process_fdt(const char **cmdp)
{
/* Allow the fdt to override the boot command */
char *env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd");
if (env)
*cmdp = env;
/*
* If the bootsecure option was chosen, use secure_boot_cmd().
* Always use 'env' in this case, since bootsecure requres that the
* bootcmd was specified in the FDT too.
*/
return fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0) != 0;
}
/*
* Runs the given boot command securely. Specifically:
* - Doesn't run the command with the shell (run_command or parse_string_outer),
* since that's a lot of code surface that an attacker might exploit.
* Because of this, we don't do any argument parsing--the secure boot command
* has to be a full-fledged u-boot command.
* - Doesn't check for keypresses before booting, since that could be a
* security hole; also disables Ctrl-C.
* - Doesn't allow the command to return.
*
* Upon any failures, this function will drop into an infinite loop after
* printing the error message to console.
*/
void cli_secure_boot_cmd(const char *cmd)
{
#ifdef CONFIG_CMDLINE
cmd_tbl_t *cmdtp;
#endif
int rc;
if (!cmd) {
printf("## Error: Secure boot command not specified\n");
goto err;
}
/* Disable Ctrl-C just in case some command is used that checks it. */
disable_ctrlc(1);
/* Find the command directly. */
#ifdef CONFIG_CMDLINE
cmdtp = find_cmd(cmd);
if (!cmdtp) {
printf("## Error: \"%s\" not defined\n", cmd);
goto err;
}
/* Run the command, forcing no flags and faking argc and argv. */
rc = (cmdtp->cmd)(cmdtp, 0, 1, (char **)&cmd);
#else
rc = board_run_command(cmd);
#endif
/* Shouldn't ever return from boot command. */
printf("## Error: \"%s\" returned (code %d)\n", cmd, rc);
err:
/*
* Not a whole lot to do here. Rebooting won't help much, since we'll
* just end up right back here. Just loop.
*/
hang();
}
#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */
void cli_loop(void)
{
#ifdef CONFIG_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#elif defined(CONFIG_CMDLINE)
cli_simple_loop();
#else
printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n");
#endif /*CONFIG_HUSH_PARSER*/
}
void cli_init(void)
{
#ifdef CONFIG_HUSH_PARSER
u_boot_hush_start();
#endif
#if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var();
#endif
}