"Das U-Boot" Source Tree
at master 342 lines 8.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2000 4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 5 * 6 * Add to readline cmdline-editing by 7 * (C) Copyright 2005 8 * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> 9 */ 10 11#define pr_fmt(fmt) "cli: %s: " fmt, __func__ 12 13#include <ansi.h> 14#include <bootstage.h> 15#include <cli.h> 16#include <cli_hush.h> 17#include <command.h> 18#include <console.h> 19#include <env.h> 20#include <fdtdec.h> 21#include <hang.h> 22#include <malloc.h> 23#include <asm/global_data.h> 24#include <dm/ofnode.h> 25#include <linux/errno.h> 26 27#ifdef CONFIG_CMDLINE 28 29static inline bool use_hush_old(void) 30{ 31 return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ? 32 gd->flags & GD_FLG_HUSH_OLD_PARSER : 33 IS_ENABLED(CONFIG_HUSH_OLD_PARSER); 34} 35 36/* 37 * Run a command using the selected parser. 38 * 39 * @param cmd Command to run 40 * @param flag Execution flags (CMD_FLAG_...) 41 * Return: 0 on success, or != 0 on error. 42 */ 43int run_command(const char *cmd, int flag) 44{ 45#if !IS_ENABLED(CONFIG_HUSH_PARSER) 46 /* 47 * cli_run_command can return 0 or 1 for success, so clean up 48 * its result. 49 */ 50 if (cli_simple_run_command(cmd, flag) == -1) 51 return 1; 52 53 return 0; 54#else 55 if (use_hush_old()) { 56 int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP; 57 58 if (flag & CMD_FLAG_ENV) 59 hush_flags |= FLAG_CONT_ON_NEWLINE; 60 return parse_string_outer(cmd, hush_flags); 61 } 62 /* 63 * Possible values for flags are the following: 64 * FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in 65 * parse_and_run_stream() after first iteration while normal 66 * behavior, * i.e. when called from cli_loop(), is to loop 67 * infinitely. 68 * FLAG_PARSE_SEMICOLON: modern Hush parses ';' and does not stop 69 * first time it sees one. So, I think we do not need this flag. 70 * FLAG_REPARSING: For the moment, I do not understand the goal 71 * of this flag. 72 * FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue 73 * parsing even when reading '\n' when coming from 74 * run_command(). In this case, modern Hush reads until it finds 75 * '\0'. So, I think we do not need this flag. 76 */ 77 return parse_string_outer_modern(cmd, FLAG_EXIT_FROM_LOOP); 78#endif 79} 80 81/* 82 * Run a command using the selected parser, and check if it is repeatable. 83 * 84 * @param cmd Command to run 85 * @param flag Execution flags (CMD_FLAG_...) 86 * Return: 0 (not repeatable) or 1 (repeatable) on success, -1 on error. 87 */ 88int run_command_repeatable(const char *cmd, int flag) 89{ 90#ifndef CONFIG_HUSH_PARSER 91 return cli_simple_run_command(cmd, flag); 92#else 93 int ret; 94 95 if (use_hush_old()) { 96 ret = parse_string_outer(cmd, 97 FLAG_PARSE_SEMICOLON 98 | FLAG_EXIT_FROM_LOOP); 99 } else { 100 ret = parse_string_outer_modern(cmd, 101 FLAG_PARSE_SEMICOLON 102 | FLAG_EXIT_FROM_LOOP); 103 } 104 105 /* 106 * parse_string_outer() returns 1 for failure, so clean up 107 * its result. 108 */ 109 if (ret) 110 return -1; 111 112 return 0; 113#endif 114} 115#else 116__weak int board_run_command(const char *cmdline) 117{ 118 printf("## Commands are disabled. Please enable CONFIG_CMDLINE.\n"); 119 120 return 1; 121} 122#endif /* CONFIG_CMDLINE */ 123 124int run_command_list(const char *cmd, int len, int flag) 125{ 126 int need_buff = 1; 127 char *buff = (char *)cmd; /* cast away const */ 128 int rcode = 0; 129 130 if (len == -1) { 131 len = strlen(cmd); 132#ifdef CONFIG_HUSH_PARSER 133 /* hush will never change our string */ 134 need_buff = 0; 135#else 136 /* the built-in parser will change our string if it sees \n */ 137 need_buff = strchr(cmd, '\n') != NULL; 138#endif 139 } 140 if (need_buff) { 141 buff = malloc(len + 1); 142 if (!buff) 143 return 1; 144 memcpy(buff, cmd, len); 145 buff[len] = '\0'; 146 } 147#ifdef CONFIG_HUSH_PARSER 148 if (use_hush_old()) { 149 rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); 150 } else { 151 rcode = parse_string_outer_modern(buff, FLAG_PARSE_SEMICOLON); 152 } 153#else 154 /* 155 * This function will overwrite any \n it sees with a \0, which 156 * is why it can't work with a const char *. Here we are making 157 * using of internal knowledge of this function, to avoid always 158 * doing a malloc() which is actually required only in a case that 159 * is pretty rare. 160 */ 161#ifdef CONFIG_CMDLINE 162 rcode = cli_simple_run_command_list(buff, flag); 163#else 164 rcode = board_run_command(buff); 165#endif 166#endif 167 if (need_buff) 168 free(buff); 169 170 return rcode; 171} 172 173int run_commandf(const char *fmt, ...) 174{ 175 va_list args; 176 int nbytes; 177 178 va_start(args, fmt); 179 /* 180 * Limit the console_buffer space being used to CONFIG_SYS_CBSIZE, 181 * because its last byte is used to fit the replacement of \0 by \n\0 182 * in underlying hush parser 183 */ 184 nbytes = vsnprintf(console_buffer, CONFIG_SYS_CBSIZE, fmt, args); 185 va_end(args); 186 187 if (nbytes < 0) { 188 pr_debug("I/O internal error occurred.\n"); 189 return -EIO; 190 } else if (nbytes >= CONFIG_SYS_CBSIZE) { 191 pr_debug("'fmt' size:%d exceeds the limit(%d)\n", 192 nbytes, CONFIG_SYS_CBSIZE); 193 return -ENOSPC; 194 } 195 return run_command(console_buffer, 0); 196} 197 198/****************************************************************************/ 199 200#if defined(CONFIG_CMD_RUN) 201int do_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) 202{ 203 int i, ret; 204 205 if (argc < 2) 206 return CMD_RET_USAGE; 207 208 for (i = 1; i < argc; ++i) { 209 char *arg; 210 211 arg = env_get(argv[i]); 212 if (arg == NULL) { 213 printf("## Error: \"%s\" not defined\n", argv[i]); 214 return 1; 215 } 216 217 ret = run_command(arg, flag | CMD_FLAG_ENV); 218 if (ret) 219 return ret; 220 } 221 return 0; 222} 223#endif 224 225#if CONFIG_IS_ENABLED(OF_CONTROL) 226bool cli_process_fdt(const char **cmdp) 227{ 228 /* Allow the fdt to override the boot command */ 229 const char *env = ofnode_conf_read_str("bootcmd"); 230 if (env) 231 *cmdp = env; 232 /* 233 * If the bootsecure option was chosen, use secure_boot_cmd(). 234 * Always use 'env' in this case, since bootsecure requres that the 235 * bootcmd was specified in the FDT too. 236 */ 237 return ofnode_conf_read_int("bootsecure", 0); 238} 239 240/* 241 * Runs the given boot command securely. Specifically: 242 * - Doesn't run the command with the shell (run_command or parse_string_outer), 243 * since that's a lot of code surface that an attacker might exploit. 244 * Because of this, we don't do any argument parsing--the secure boot command 245 * has to be a full-fledged u-boot command. 246 * - Doesn't check for keypresses before booting, since that could be a 247 * security hole; also disables Ctrl-C. 248 * - Doesn't allow the command to return. 249 * 250 * Upon any failures, this function will drop into an infinite loop after 251 * printing the error message to console. 252 */ 253void cli_secure_boot_cmd(const char *cmd) 254{ 255#ifdef CONFIG_CMDLINE 256 struct cmd_tbl *cmdtp; 257#endif 258 int rc; 259 260 if (!cmd) { 261 printf("## Error: Secure boot command not specified\n"); 262 goto err; 263 } 264 265 /* Disable Ctrl-C just in case some command is used that checks it. */ 266 disable_ctrlc(1); 267 268 /* Find the command directly. */ 269#ifdef CONFIG_CMDLINE 270 cmdtp = find_cmd(cmd); 271 if (!cmdtp) { 272 printf("## Error: \"%s\" not defined\n", cmd); 273 goto err; 274 } 275 276 /* Run the command, forcing no flags and faking argc and argv. */ 277 rc = (cmdtp->cmd)(cmdtp, 0, 1, (char **)&cmd); 278 279#else 280 rc = board_run_command(cmd); 281#endif 282 283 /* Shouldn't ever return from boot command. */ 284 printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); 285 286err: 287 /* 288 * Not a whole lot to do here. Rebooting won't help much, since we'll 289 * just end up right back here. Just loop. 290 */ 291 hang(); 292} 293#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ 294 295void cli_loop(void) 296{ 297 bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP); 298#if CONFIG_IS_ENABLED(HUSH_PARSER) 299 if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) 300 parse_and_run_file(); 301 else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) 302 parse_file_outer(); 303 304 printf("Problem\n"); 305 /* This point is never reached */ 306 for (;;); 307#elif defined(CONFIG_CMDLINE) 308 cli_simple_loop(); 309#else 310 printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n"); 311#endif /*CONFIG_HUSH_PARSER*/ 312} 313 314void cli_init(void) 315{ 316#ifdef CONFIG_HUSH_PARSER 317 /* This if block is used to initialize hush parser gd flag. */ 318 if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER) 319 && !(gd->flags & GD_FLG_HUSH_MODERN_PARSER)) { 320 if (CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) 321 gd->flags |= GD_FLG_HUSH_OLD_PARSER; 322 else if (CONFIG_IS_ENABLED(HUSH_MODERN_PARSER)) 323 gd->flags |= GD_FLG_HUSH_MODERN_PARSER; 324 } 325 326 if (gd->flags & GD_FLG_HUSH_OLD_PARSER) { 327 u_boot_hush_start(); 328 } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) { 329 u_boot_hush_start_modern(); 330 } else { 331 printf("No valid hush parser to use, cli will not initialized!\n"); 332 return; 333 } 334#endif 335 336#if defined(CONFIG_HUSH_INIT_VAR) 337 hush_init_var(); 338#endif 339 340 if (CONFIG_IS_ENABLED(VIDEO_ANSI)) 341 printf(ANSI_CURSOR_SHOW "\n"); 342}