Merge tag 'u-boot-dfu-20240111' of https://source.denx.de/u-boot/custodians/u-boot-dfu
u-boot-dfu-20240111 - Implement fastboot multi-response. This allows multi-line response and most importantly, finally adds support for fastboot getvar all command. - New 'fastboot oem console' command. Useful for debugging to send data the u-boot shell via fastboot - Console recording fixes
This commit is contained in:
commit
d3dba8a28b
|
@ -82,7 +82,7 @@ static int extlinux_fill_info(struct bootflow *bflow)
|
|||
log_debug("parsing bflow file size %x\n", bflow->size);
|
||||
membuff_init(&mb, bflow->buf, bflow->size);
|
||||
membuff_putraw(&mb, bflow->size, true, &data);
|
||||
while (len = membuff_readline(&mb, line, sizeof(line) - 1, ' '), len) {
|
||||
while (len = membuff_readline(&mb, line, sizeof(line) - 1, ' ', true), len) {
|
||||
char *tok, *p = line;
|
||||
|
||||
tok = strsep(&p, " ");
|
||||
|
|
|
@ -821,6 +821,9 @@ int console_record_init(void)
|
|||
ret = membuff_new((struct membuff *)&gd->console_in,
|
||||
CONFIG_CONSOLE_RECORD_IN_SIZE);
|
||||
|
||||
/* Start recording from the beginning */
|
||||
gd->flags |= GD_FLG_RECORD;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -845,7 +848,7 @@ int console_record_readline(char *str, int maxlen)
|
|||
return -ENOSPC;
|
||||
|
||||
return membuff_readline((struct membuff *)&gd->console_out, str,
|
||||
maxlen, '\0');
|
||||
maxlen, '\0', false);
|
||||
}
|
||||
|
||||
int console_record_avail(void)
|
||||
|
@ -853,6 +856,11 @@ int console_record_avail(void)
|
|||
return membuff_avail((struct membuff *)&gd->console_out);
|
||||
}
|
||||
|
||||
bool console_record_isempty(void)
|
||||
{
|
||||
return membuff_isempty((struct membuff *)&gd->console_out);
|
||||
}
|
||||
|
||||
int console_in_puts(const char *str)
|
||||
{
|
||||
return membuff_put((struct membuff *)&gd->console_in, str, strlen(str));
|
||||
|
|
|
@ -173,6 +173,9 @@ The various currently defined names are::
|
|||
bootloader requiring a signature before
|
||||
it will install or boot images.
|
||||
|
||||
all Provides all info from commands above as
|
||||
they were called one by one
|
||||
|
||||
Names starting with a lowercase character are reserved by this
|
||||
specification. OEM-specific names should not start with lowercase
|
||||
characters.
|
||||
|
|
|
@ -29,6 +29,7 @@ The following OEM commands are supported (if enabled):
|
|||
with <arg> = boot_ack boot_partition
|
||||
- ``oem bootbus`` - this executes ``mmc bootbus %x %s`` to configure eMMC
|
||||
- ``oem run`` - this executes an arbitrary U-Boot command
|
||||
- ``oem console`` - this dumps U-Boot console record buffer
|
||||
|
||||
Support for both eMMC and NAND devices is included.
|
||||
|
||||
|
|
|
@ -242,6 +242,13 @@ config FASTBOOT_OEM_RUN
|
|||
this feature if you are using verified boot, as it will allow an
|
||||
attacker to bypass any restrictions you have in place.
|
||||
|
||||
config FASTBOOT_CMD_OEM_CONSOLE
|
||||
bool "Enable the 'oem console' command"
|
||||
depends on CONSOLE_RECORD
|
||||
help
|
||||
Add support for the "oem console" command to input and read console
|
||||
record buffer.
|
||||
|
||||
endif # FASTBOOT
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <console.h>
|
||||
#include <env.h>
|
||||
#include <fastboot.h>
|
||||
#include <fastboot-internal.h>
|
||||
|
@ -40,6 +41,7 @@ static void reboot_recovery(char *, char *);
|
|||
static void oem_format(char *, char *);
|
||||
static void oem_partconf(char *, char *);
|
||||
static void oem_bootbus(char *, char *);
|
||||
static void oem_console(char *, char *);
|
||||
static void run_ucmd(char *, char *);
|
||||
static void run_acmd(char *, char *);
|
||||
|
||||
|
@ -107,6 +109,10 @@ static const struct {
|
|||
.command = "oem run",
|
||||
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL))
|
||||
},
|
||||
[FASTBOOT_COMMAND_OEM_CONSOLE] = {
|
||||
.command = "oem console",
|
||||
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_CONSOLE, (oem_console), (NULL))
|
||||
},
|
||||
[FASTBOOT_COMMAND_UCMD] = {
|
||||
.command = "UCmd",
|
||||
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL))
|
||||
|
@ -152,6 +158,35 @@ int fastboot_handle_command(char *cmd_string, char *response)
|
|||
return -1;
|
||||
}
|
||||
|
||||
void fastboot_multiresponse(int cmd, char *response)
|
||||
{
|
||||
switch (cmd) {
|
||||
case FASTBOOT_COMMAND_GETVAR:
|
||||
fastboot_getvar_all(response);
|
||||
break;
|
||||
case FASTBOOT_COMMAND_OEM_CONSOLE:
|
||||
if (CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_CONSOLE)) {
|
||||
char buf[FASTBOOT_RESPONSE_LEN] = { 0 };
|
||||
|
||||
if (console_record_isempty()) {
|
||||
console_record_reset();
|
||||
fastboot_okay(NULL, response);
|
||||
} else {
|
||||
int ret = console_record_readline(buf, sizeof(buf) - 5);
|
||||
|
||||
if (ret < 0)
|
||||
fastboot_fail("Error reading console", response);
|
||||
else
|
||||
fastboot_response("INFO", response, "%s", buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fastboot_fail("Unknown multiresponse command", response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* okay() - Send bare OKAY response
|
||||
*
|
||||
|
@ -490,3 +525,20 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response)
|
|||
else
|
||||
fastboot_okay(NULL, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* oem_console() - Execute the OEM console command
|
||||
*
|
||||
* @cmd_parameter: Pointer to command parameter
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
static void __maybe_unused oem_console(char *cmd_parameter, char *response)
|
||||
{
|
||||
if (cmd_parameter)
|
||||
console_in_puts(cmd_parameter);
|
||||
|
||||
if (console_record_isempty())
|
||||
fastboot_fail("Empty console", response);
|
||||
else
|
||||
fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL);
|
||||
}
|
||||
|
|
|
@ -29,53 +29,67 @@ static void getvar_is_userspace(char *var_parameter, char *response);
|
|||
|
||||
static const struct {
|
||||
const char *variable;
|
||||
bool list;
|
||||
void (*dispatch)(char *var_parameter, char *response);
|
||||
} getvar_dispatch[] = {
|
||||
{
|
||||
.variable = "version",
|
||||
.dispatch = getvar_version
|
||||
.dispatch = getvar_version,
|
||||
.list = true,
|
||||
}, {
|
||||
.variable = "version-bootloader",
|
||||
.dispatch = getvar_version_bootloader
|
||||
.dispatch = getvar_version_bootloader,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "downloadsize",
|
||||
.dispatch = getvar_downloadsize
|
||||
.dispatch = getvar_downloadsize,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "max-download-size",
|
||||
.dispatch = getvar_downloadsize
|
||||
.dispatch = getvar_downloadsize,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "serialno",
|
||||
.dispatch = getvar_serialno
|
||||
.dispatch = getvar_serialno,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "version-baseband",
|
||||
.dispatch = getvar_version_baseband
|
||||
.dispatch = getvar_version_baseband,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "product",
|
||||
.dispatch = getvar_product
|
||||
.dispatch = getvar_product,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "platform",
|
||||
.dispatch = getvar_platform
|
||||
.dispatch = getvar_platform,
|
||||
.list = true
|
||||
}, {
|
||||
.variable = "current-slot",
|
||||
.dispatch = getvar_current_slot
|
||||
.dispatch = getvar_current_slot,
|
||||
.list = true
|
||||
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
|
||||
}, {
|
||||
.variable = "has-slot",
|
||||
.dispatch = getvar_has_slot
|
||||
.dispatch = getvar_has_slot,
|
||||
.list = false
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)
|
||||
}, {
|
||||
.variable = "partition-type",
|
||||
.dispatch = getvar_partition_type
|
||||
.dispatch = getvar_partition_type,
|
||||
.list = false
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
|
||||
}, {
|
||||
.variable = "partition-size",
|
||||
.dispatch = getvar_partition_size
|
||||
.dispatch = getvar_partition_size,
|
||||
.list = false
|
||||
#endif
|
||||
}, {
|
||||
.variable = "is-userspace",
|
||||
.dispatch = getvar_is_userspace
|
||||
.dispatch = getvar_is_userspace,
|
||||
.list = true
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -237,6 +251,40 @@ static void getvar_is_userspace(char *var_parameter, char *response)
|
|||
fastboot_okay("no", response);
|
||||
}
|
||||
|
||||
static int current_all_dispatch;
|
||||
void fastboot_getvar_all(char *response)
|
||||
{
|
||||
/*
|
||||
* Find a dispatch getvar that can be listed and send
|
||||
* it as INFO until we reach the end.
|
||||
*/
|
||||
while (current_all_dispatch < ARRAY_SIZE(getvar_dispatch)) {
|
||||
if (!getvar_dispatch[current_all_dispatch].list) {
|
||||
current_all_dispatch++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char envstr[FASTBOOT_RESPONSE_LEN] = { 0 };
|
||||
|
||||
getvar_dispatch[current_all_dispatch].dispatch(NULL, envstr);
|
||||
|
||||
char *envstr_start = envstr;
|
||||
|
||||
if (!strncmp("OKAY", envstr, 4) || !strncmp("FAIL", envstr, 4))
|
||||
envstr_start += 4;
|
||||
|
||||
fastboot_response("INFO", response, "%s: %s",
|
||||
getvar_dispatch[current_all_dispatch].variable,
|
||||
envstr_start);
|
||||
|
||||
current_all_dispatch++;
|
||||
return;
|
||||
}
|
||||
|
||||
fastboot_response("OKAY", response, NULL);
|
||||
current_all_dispatch = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
|
||||
*
|
||||
|
@ -254,6 +302,9 @@ void fastboot_getvar(char *cmd_parameter, char *response)
|
|||
{
|
||||
if (!cmd_parameter) {
|
||||
fastboot_fail("missing var", response);
|
||||
} else if (!strncmp("all", cmd_parameter, 3) && strlen(cmd_parameter) == 3) {
|
||||
current_all_dispatch = 0;
|
||||
fastboot_response(FASTBOOT_MULTIRESPONSE_START, response, NULL);
|
||||
} else {
|
||||
#define FASTBOOT_ENV_PREFIX "fastboot."
|
||||
int i;
|
||||
|
|
|
@ -497,6 +497,25 @@ static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
|
|||
do_exit_on_complete(ep, req);
|
||||
}
|
||||
|
||||
static int multiresponse_cmd = -1;
|
||||
static void multiresponse_on_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
char response[FASTBOOT_RESPONSE_LEN] = {0};
|
||||
|
||||
if (multiresponse_cmd == -1)
|
||||
return;
|
||||
|
||||
/* Call handler to obtain next response */
|
||||
fastboot_multiresponse(multiresponse_cmd, response);
|
||||
fastboot_tx_write_str(response);
|
||||
|
||||
/* If response is final OKAY/FAIL response disconnect this handler and unset cmd */
|
||||
if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) {
|
||||
multiresponse_cmd = -1;
|
||||
fastboot_func->in_req->complete = fastboot_complete;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_acmd_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
/* When usb dequeue complete will be called
|
||||
|
@ -524,6 +543,16 @@ static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
|
|||
fastboot_fail("buffer overflow", response);
|
||||
}
|
||||
|
||||
if (!strncmp(FASTBOOT_MULTIRESPONSE_START, response, 4)) {
|
||||
multiresponse_cmd = cmd;
|
||||
fastboot_multiresponse(multiresponse_cmd, response);
|
||||
|
||||
/* Only add complete callback if first is not a final OKAY/FAIL response */
|
||||
if (strncmp("OKAY", response, 4) && strncmp("FAIL", response, 4)) {
|
||||
fastboot_func->in_req->complete = multiresponse_on_complete;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncmp("DATA", response, 4)) {
|
||||
req->complete = rx_handler_dl_image;
|
||||
req->length = rx_bytes_expected(ep);
|
||||
|
|
|
@ -84,6 +84,13 @@ int console_record_readline(char *str, int maxlen);
|
|||
*/
|
||||
int console_record_avail(void);
|
||||
|
||||
/**
|
||||
* console_record_isempty() - Returns if console output is empty
|
||||
*
|
||||
* Return: true if empty
|
||||
*/
|
||||
bool console_record_isempty(void);
|
||||
|
||||
/**
|
||||
* console_in_puts() - Write a string to the console input buffer
|
||||
*
|
||||
|
@ -131,6 +138,12 @@ static inline int console_in_puts(const char *str)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline bool console_record_isempty(void)
|
||||
{
|
||||
/* Always empty */
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_CONSOLE_RECORD */
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,6 +18,13 @@ extern u32 fastboot_buf_size;
|
|||
*/
|
||||
extern void (*fastboot_progress_callback)(const char *msg);
|
||||
|
||||
/**
|
||||
* fastboot_getvar_all() - Writes current variable being listed from "all" to response.
|
||||
*
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
void fastboot_getvar_all(char *response);
|
||||
|
||||
/**
|
||||
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
|
||||
*
|
||||
|
|
|
@ -14,6 +14,16 @@
|
|||
|
||||
#define FASTBOOT_VERSION "0.4"
|
||||
|
||||
/*
|
||||
* Signals u-boot fastboot code to send multiple responses by
|
||||
* calling response generating function repeatedly until a OKAY/FAIL
|
||||
* is generated as final response.
|
||||
*
|
||||
* This status code is only used internally to signal, must NOT
|
||||
* be sent to host.
|
||||
*/
|
||||
#define FASTBOOT_MULTIRESPONSE_START ("MORE")
|
||||
|
||||
/* The 64 defined bytes plus \0 */
|
||||
#define FASTBOOT_COMMAND_LEN (64 + 1)
|
||||
#define FASTBOOT_RESPONSE_LEN (64 + 1)
|
||||
|
@ -37,6 +47,7 @@ enum {
|
|||
FASTBOOT_COMMAND_OEM_PARTCONF,
|
||||
FASTBOOT_COMMAND_OEM_BOOTBUS,
|
||||
FASTBOOT_COMMAND_OEM_RUN,
|
||||
FASTBOOT_COMMAND_OEM_CONSOLE,
|
||||
FASTBOOT_COMMAND_ACMD,
|
||||
FASTBOOT_COMMAND_UCMD,
|
||||
FASTBOOT_COMMAND_COUNT
|
||||
|
@ -172,5 +183,13 @@ void fastboot_data_download(const void *fastboot_data,
|
|||
*/
|
||||
void fastboot_data_complete(char *response);
|
||||
|
||||
/**
|
||||
* fastboot_handle_multiresponse() - Called for each response to send
|
||||
*
|
||||
* @cmd: Command id that requested multiresponse
|
||||
* @response: Pointer to fastboot response buffer
|
||||
*/
|
||||
void fastboot_multiresponse(int cmd, char *response);
|
||||
|
||||
void fastboot_acmd_complete(void);
|
||||
#endif /* _FASTBOOT_H_ */
|
||||
|
|
|
@ -192,10 +192,11 @@ int membuff_free(struct membuff *mb);
|
|||
* @mb: membuff to adjust
|
||||
* @str: Place to put the line
|
||||
* @maxlen: Maximum line length (excluding terminator)
|
||||
* @must_fit: If true then str is empty if line doesn't fit
|
||||
* Return: number of bytes read (including terminator) if a line has been
|
||||
* read, 0 if nothing was there
|
||||
* read, 0 if nothing was there or line didn't fit when must_fit is set
|
||||
*/
|
||||
int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch);
|
||||
int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch, bool must_fit);
|
||||
|
||||
/**
|
||||
* membuff_extend_by() - expand a membuff
|
||||
|
|
|
@ -287,7 +287,7 @@ int membuff_free(struct membuff *mb)
|
|||
(mb->end - mb->start) - 1 - membuff_avail(mb);
|
||||
}
|
||||
|
||||
int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch)
|
||||
int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch, bool must_fit)
|
||||
{
|
||||
int len; /* number of bytes read (!= string length) */
|
||||
char *s, *end;
|
||||
|
@ -309,7 +309,7 @@ int membuff_readline(struct membuff *mb, char *str, int maxlen, int minch)
|
|||
}
|
||||
|
||||
/* couldn't get the whole string */
|
||||
if (!ok) {
|
||||
if (!ok && must_fit) {
|
||||
if (maxlen)
|
||||
*orig = '\0';
|
||||
return 0;
|
||||
|
|
|
@ -42,16 +42,15 @@ static int fastboot_remote_port;
|
|||
static int fastboot_our_port;
|
||||
|
||||
/**
|
||||
* fastboot_udp_send_info() - Send an INFO packet during long commands.
|
||||
* fastboot_udp_send_response() - Send an response into UDP
|
||||
*
|
||||
* @msg: String describing the reason for waiting
|
||||
* @response: Response to send
|
||||
*/
|
||||
static void fastboot_udp_send_info(const char *msg)
|
||||
static void fastboot_udp_send_response(const char *response)
|
||||
{
|
||||
uchar *packet;
|
||||
uchar *packet_base;
|
||||
int len = 0;
|
||||
char response[FASTBOOT_RESPONSE_LEN] = {0};
|
||||
|
||||
struct fastboot_header response_header = {
|
||||
.id = FASTBOOT_FASTBOOT,
|
||||
|
@ -66,7 +65,6 @@ static void fastboot_udp_send_info(const char *msg)
|
|||
memcpy(packet, &response_header, sizeof(response_header));
|
||||
packet += sizeof(response_header);
|
||||
/* Write response */
|
||||
fastboot_response("INFO", response, "%s", msg);
|
||||
memcpy(packet, response, strlen(response));
|
||||
packet += strlen(response);
|
||||
|
||||
|
@ -91,6 +89,7 @@ static void fastboot_udp_send_info(const char *msg)
|
|||
static void fastboot_timed_send_info(const char *msg)
|
||||
{
|
||||
static ulong start;
|
||||
char response[FASTBOOT_RESPONSE_LEN] = {0};
|
||||
|
||||
/* Initialize timer */
|
||||
if (start == 0)
|
||||
|
@ -99,7 +98,8 @@ static void fastboot_timed_send_info(const char *msg)
|
|||
/* Send INFO packet to host every 30 seconds */
|
||||
if (time >= 30000) {
|
||||
start = get_timer(0);
|
||||
fastboot_udp_send_info(msg);
|
||||
fastboot_response("INFO", response, "%s", msg);
|
||||
fastboot_udp_send_response(response);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,23 @@ static void fastboot_send(struct fastboot_header header, char *fastboot_data,
|
|||
} else {
|
||||
cmd = fastboot_handle_command(command, response);
|
||||
pending_command = false;
|
||||
|
||||
if (!strncmp(FASTBOOT_MULTIRESPONSE_START, response, 4)) {
|
||||
while (1) {
|
||||
/* Call handler to obtain next response */
|
||||
fastboot_multiresponse(cmd, response);
|
||||
|
||||
/*
|
||||
* Send more responses or break to send
|
||||
* final OKAY/FAIL response
|
||||
*/
|
||||
if (strncmp("OKAY", response, 4) &&
|
||||
strncmp("FAIL", response, 4))
|
||||
fastboot_udp_send_response(response);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Sent some INFO packets, need to update sequence number in
|
||||
|
|
|
@ -53,29 +53,12 @@ static int hush_test_simple_dollar(struct unit_test_state *uts)
|
|||
ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
|
||||
/* Next line contains error message */
|
||||
ut_assert_skipline();
|
||||
|
||||
if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
|
||||
/*
|
||||
* For some strange reasons, the console is not empty after
|
||||
* running above command.
|
||||
* So, we reset it to not have side effects for other tests.
|
||||
*/
|
||||
console_record_reset_enable();
|
||||
} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
|
||||
ut_assert_console_end();
|
||||
}
|
||||
ut_assert_console_end();
|
||||
|
||||
ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
|
||||
/* Two next lines contain error message */
|
||||
/* Next line contains error message */
|
||||
ut_assert_skipline();
|
||||
ut_assert_skipline();
|
||||
|
||||
if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
|
||||
/* See above comments. */
|
||||
console_record_reset_enable();
|
||||
} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
|
||||
ut_assert_console_end();
|
||||
}
|
||||
ut_assert_console_end();
|
||||
|
||||
ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user