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:
Tom Rini 2024-01-11 10:03:51 -05:00
commit d3dba8a28b
15 changed files with 236 additions and 45 deletions

View File

@ -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, " ");

View File

@ -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));

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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 */
/**

View File

@ -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.
*

View File

@ -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_ */

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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));