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
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); log_debug("parsing bflow file size %x\n", bflow->size);
membuff_init(&mb, bflow->buf, bflow->size); membuff_init(&mb, bflow->buf, bflow->size);
membuff_putraw(&mb, bflow->size, true, &data); 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; char *tok, *p = line;
tok = strsep(&p, " "); tok = strsep(&p, " ");

View File

@@ -821,6 +821,9 @@ int console_record_init(void)
ret = membuff_new((struct membuff *)&gd->console_in, ret = membuff_new((struct membuff *)&gd->console_in,
CONFIG_CONSOLE_RECORD_IN_SIZE); CONFIG_CONSOLE_RECORD_IN_SIZE);
/* Start recording from the beginning */
gd->flags |= GD_FLG_RECORD;
return ret; return ret;
} }
@@ -845,7 +848,7 @@ int console_record_readline(char *str, int maxlen)
return -ENOSPC; return -ENOSPC;
return membuff_readline((struct membuff *)&gd->console_out, str, return membuff_readline((struct membuff *)&gd->console_out, str,
maxlen, '\0'); maxlen, '\0', false);
} }
int console_record_avail(void) int console_record_avail(void)
@@ -853,6 +856,11 @@ int console_record_avail(void)
return membuff_avail((struct membuff *)&gd->console_out); 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) int console_in_puts(const char *str)
{ {
return membuff_put((struct membuff *)&gd->console_in, str, strlen(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 bootloader requiring a signature before
it will install or boot images. 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 Names starting with a lowercase character are reserved by this
specification. OEM-specific names should not start with lowercase specification. OEM-specific names should not start with lowercase
characters. characters.

View File

@@ -29,6 +29,7 @@ The following OEM commands are supported (if enabled):
with <arg> = boot_ack boot_partition with <arg> = boot_ack boot_partition
- ``oem bootbus`` - this executes ``mmc bootbus %x %s`` to configure eMMC - ``oem bootbus`` - this executes ``mmc bootbus %x %s`` to configure eMMC
- ``oem run`` - this executes an arbitrary U-Boot command - ``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. 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 this feature if you are using verified boot, as it will allow an
attacker to bypass any restrictions you have in place. 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 endif # FASTBOOT
endmenu endmenu

View File

@@ -5,6 +5,7 @@
#include <common.h> #include <common.h>
#include <command.h> #include <command.h>
#include <console.h>
#include <env.h> #include <env.h>
#include <fastboot.h> #include <fastboot.h>
#include <fastboot-internal.h> #include <fastboot-internal.h>
@@ -40,6 +41,7 @@ static void reboot_recovery(char *, char *);
static void oem_format(char *, char *); static void oem_format(char *, char *);
static void oem_partconf(char *, char *); static void oem_partconf(char *, char *);
static void oem_bootbus(char *, char *); static void oem_bootbus(char *, char *);
static void oem_console(char *, char *);
static void run_ucmd(char *, char *); static void run_ucmd(char *, char *);
static void run_acmd(char *, char *); static void run_acmd(char *, char *);
@@ -107,6 +109,10 @@ static const struct {
.command = "oem run", .command = "oem run",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_OEM_RUN, (run_ucmd), (NULL)) .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] = { [FASTBOOT_COMMAND_UCMD] = {
.command = "UCmd", .command = "UCmd",
.dispatch = CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT, (run_ucmd), (NULL)) .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; 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 * okay() - Send bare OKAY response
* *
@@ -490,3 +525,20 @@ static void __maybe_unused oem_bootbus(char *cmd_parameter, char *response)
else else
fastboot_okay(NULL, response); 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 { static const struct {
const char *variable; const char *variable;
bool list;
void (*dispatch)(char *var_parameter, char *response); void (*dispatch)(char *var_parameter, char *response);
} getvar_dispatch[] = { } getvar_dispatch[] = {
{ {
.variable = "version", .variable = "version",
.dispatch = getvar_version .dispatch = getvar_version,
.list = true,
}, { }, {
.variable = "version-bootloader", .variable = "version-bootloader",
.dispatch = getvar_version_bootloader .dispatch = getvar_version_bootloader,
.list = true
}, { }, {
.variable = "downloadsize", .variable = "downloadsize",
.dispatch = getvar_downloadsize .dispatch = getvar_downloadsize,
.list = true
}, { }, {
.variable = "max-download-size", .variable = "max-download-size",
.dispatch = getvar_downloadsize .dispatch = getvar_downloadsize,
.list = true
}, { }, {
.variable = "serialno", .variable = "serialno",
.dispatch = getvar_serialno .dispatch = getvar_serialno,
.list = true
}, { }, {
.variable = "version-baseband", .variable = "version-baseband",
.dispatch = getvar_version_baseband .dispatch = getvar_version_baseband,
.list = true
}, { }, {
.variable = "product", .variable = "product",
.dispatch = getvar_product .dispatch = getvar_product,
.list = true
}, { }, {
.variable = "platform", .variable = "platform",
.dispatch = getvar_platform .dispatch = getvar_platform,
.list = true
}, { }, {
.variable = "current-slot", .variable = "current-slot",
.dispatch = getvar_current_slot .dispatch = getvar_current_slot,
.list = true
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH) #if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
}, { }, {
.variable = "has-slot", .variable = "has-slot",
.dispatch = getvar_has_slot .dispatch = getvar_has_slot,
.list = false
#endif #endif
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC) #if IS_ENABLED(CONFIG_FASTBOOT_FLASH_MMC)
}, { }, {
.variable = "partition-type", .variable = "partition-type",
.dispatch = getvar_partition_type .dispatch = getvar_partition_type,
.list = false
#endif #endif
#if IS_ENABLED(CONFIG_FASTBOOT_FLASH) #if IS_ENABLED(CONFIG_FASTBOOT_FLASH)
}, { }, {
.variable = "partition-size", .variable = "partition-size",
.dispatch = getvar_partition_size .dispatch = getvar_partition_size,
.list = false
#endif #endif
}, { }, {
.variable = "is-userspace", .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); 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. * 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) { if (!cmd_parameter) {
fastboot_fail("missing var", response); 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 { } else {
#define FASTBOOT_ENV_PREFIX "fastboot." #define FASTBOOT_ENV_PREFIX "fastboot."
int i; 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); 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) static void do_acmd_complete(struct usb_ep *ep, struct usb_request *req)
{ {
/* When usb dequeue complete will be called /* 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); 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)) { if (!strncmp("DATA", response, 4)) {
req->complete = rx_handler_dl_image; req->complete = rx_handler_dl_image;
req->length = rx_bytes_expected(ep); 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); 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 * 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; return 0;
} }
static inline bool console_record_isempty(void)
{
/* Always empty */
return true;
}
#endif /* !CONFIG_CONSOLE_RECORD */ #endif /* !CONFIG_CONSOLE_RECORD */
/** /**

View File

@@ -18,6 +18,13 @@ extern u32 fastboot_buf_size;
*/ */
extern void (*fastboot_progress_callback)(const char *msg); 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. * fastboot_getvar() - Writes variable indicated by cmd_parameter to response.
* *

View File

@@ -14,6 +14,16 @@
#define FASTBOOT_VERSION "0.4" #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 */ /* The 64 defined bytes plus \0 */
#define FASTBOOT_COMMAND_LEN (64 + 1) #define FASTBOOT_COMMAND_LEN (64 + 1)
#define FASTBOOT_RESPONSE_LEN (64 + 1) #define FASTBOOT_RESPONSE_LEN (64 + 1)
@@ -37,6 +47,7 @@ enum {
FASTBOOT_COMMAND_OEM_PARTCONF, FASTBOOT_COMMAND_OEM_PARTCONF,
FASTBOOT_COMMAND_OEM_BOOTBUS, FASTBOOT_COMMAND_OEM_BOOTBUS,
FASTBOOT_COMMAND_OEM_RUN, FASTBOOT_COMMAND_OEM_RUN,
FASTBOOT_COMMAND_OEM_CONSOLE,
FASTBOOT_COMMAND_ACMD, FASTBOOT_COMMAND_ACMD,
FASTBOOT_COMMAND_UCMD, FASTBOOT_COMMAND_UCMD,
FASTBOOT_COMMAND_COUNT FASTBOOT_COMMAND_COUNT
@@ -172,5 +183,13 @@ void fastboot_data_download(const void *fastboot_data,
*/ */
void fastboot_data_complete(char *response); 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); void fastboot_acmd_complete(void);
#endif /* _FASTBOOT_H_ */ #endif /* _FASTBOOT_H_ */

View File

@@ -192,10 +192,11 @@ int membuff_free(struct membuff *mb);
* @mb: membuff to adjust * @mb: membuff to adjust
* @str: Place to put the line * @str: Place to put the line
* @maxlen: Maximum line length (excluding terminator) * @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 * 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 * 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); (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) */ int len; /* number of bytes read (!= string length) */
char *s, *end; 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 */ /* couldn't get the whole string */
if (!ok) { if (!ok && must_fit) {
if (maxlen) if (maxlen)
*orig = '\0'; *orig = '\0';
return 0; return 0;

View File

@@ -42,16 +42,15 @@ static int fastboot_remote_port;
static int fastboot_our_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;
uchar *packet_base; uchar *packet_base;
int len = 0; int len = 0;
char response[FASTBOOT_RESPONSE_LEN] = {0};
struct fastboot_header response_header = { struct fastboot_header response_header = {
.id = FASTBOOT_FASTBOOT, .id = FASTBOOT_FASTBOOT,
@@ -66,7 +65,6 @@ static void fastboot_udp_send_info(const char *msg)
memcpy(packet, &response_header, sizeof(response_header)); memcpy(packet, &response_header, sizeof(response_header));
packet += sizeof(response_header); packet += sizeof(response_header);
/* Write response */ /* Write response */
fastboot_response("INFO", response, "%s", msg);
memcpy(packet, response, strlen(response)); memcpy(packet, response, strlen(response));
packet += 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 void fastboot_timed_send_info(const char *msg)
{ {
static ulong start; static ulong start;
char response[FASTBOOT_RESPONSE_LEN] = {0};
/* Initialize timer */ /* Initialize timer */
if (start == 0) if (start == 0)
@@ -99,7 +98,8 @@ static void fastboot_timed_send_info(const char *msg)
/* Send INFO packet to host every 30 seconds */ /* Send INFO packet to host every 30 seconds */
if (time >= 30000) { if (time >= 30000) {
start = get_timer(0); 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 { } else {
cmd = fastboot_handle_command(command, response); cmd = fastboot_handle_command(command, response);
pending_command = false; 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 * 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)); ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
/* Next line contains error message */ /* Next line contains error message */
ut_assert_skipline(); 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)); 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();
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)); ut_assertok(run_command("dollar_foo='bar \"quux'", 0));