[PATCH 00/24] boot: efi: Various improvements to booting with the EFI app

From: Simon Glass <sjg@chromium.org> This series provides a number of tweaks which make it easier to test booting with the EFI app, particularly on ARM: - 'fake go' in bootstd - more debugging in various places - tidy up the generally incorrect ATAGs error - drop a panic() in the bowels of boot - ensure booti does not overwrite memory when relocating It also includes a way to quit or disable the pager, which is very helpful when running the app on a machine with no serial console. Finally, a bug in the release script is causing CI to fail, so a few patches deal with that. Simon Glass (24): doc: Resolve a formatting error in upcoming releases scripts: Fix RST formatting in release_version.py console: Restore old pager test-bypass value console: Correct condition for setting enabled pager bypass console: Add bypass keypress to disable paging console: Add a way to quit pager with no further output boot: Provide a Kconfig to enable faking the boot boot: Select the fake-go state explicitly boot: Tidy up the bootflow flags boot: Provide a bootflow option to fake a boot boot: Drop hex prefix from the booti image-moving message boot: Show the ramdisk node when debugging boot: Support a fake go with pxe boot: Add more debugging around devicetree selection boot: Fix code formatting in fit_image_load() boot: Add more debugging in FIT-handling arm: Update boot_prep_linux() to return an error arm: bootm: Fix a confusing message about ATAGs lmb: Provide a static inline for lmb_alloc() arm: Use lmb when relocating the image efi: Include efi.h in the efi_variable.h header efi: app: Call efi_exit() if board_init_r() returns efi: app: Move efi_main() to the bottom of the file efi: app: arm: Add various options to the build arch/arm/lib/bootm.c | 22 +++- arch/arm/lib/image.c | 15 ++- boot/Kconfig | 8 ++ boot/bootm.c | 16 ++- boot/ext_pxe_common.c | 4 + boot/image-board.c | 7 +- boot/image-fdt.c | 3 + boot/image-fit.c | 12 +- boot/pxe_utils.c | 21 +++- cmd/bootflow.c | 16 ++- cmd/booti.c | 2 +- common/cli_readline.c | 1 + common/console.c | 2 +- common/pager.c | 56 +++++++-- configs/efi-arm_app64_defconfig | 5 + doc/develop/concept_releases.rst | 3 +- doc/usage/cmd/bootflow.rst | 3 + doc/usage/cmd/bootm.rst | 3 +- doc/usage/console.rst | 6 +- include/bootflow.h | 14 ++- include/efi_variable.h | 1 + include/image.h | 3 +- include/lmb.h | 9 ++ include/pager.h | 22 ++++ include/pxe_utils.h | 2 + lib/efi_client/efi_app.c | 113 +++++++++--------- scripts/release_version.py | 3 +- test/common/pager.c | 93 ++++++++++++++- test/scripts/test_release_version.py | 164 +++++++++++++++++++++++++++ test/test-main.c | 6 +- 30 files changed, 519 insertions(+), 116 deletions(-) -- 2.43.0 base-commit: 05cebf0bdd955fa267b1533bbf145499f0af63b9 branch: loadw

From: Simon Glass <sjg@chromium.org> When there is a final release, it should be on the same line. Fix this. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/concept_releases.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/develop/concept_releases.rst b/doc/develop/concept_releases.rst index e11bf9042b2..aa4f189f1f4 100644 --- a/doc/develop/concept_releases.rst +++ b/doc/develop/concept_releases.rst @@ -38,8 +38,7 @@ Release candidate schedule: * **2025.10-rc3**: Mon 25-Aug-2025 * **2025.10-rc2**: Mon 08-Sep-2025 * **2025.10-rc1**: Mon 22-Sep-2025 -* **2025.10** (Final): \ -Mon 06-Oct-2025 +* **2025.10** (Final): Mon 06-Oct-2025 Release History --------------- -- 2.43.0

From: Simon Glass <sjg@chromium.org> Fix line continuation in generate_schedule() that caused Sphinx to fail with "Bullet list ends without a blank line; unexpected unindent" error. Add tests to validate RST formatting of generated documentation. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/release_version.py | 3 +- test/scripts/test_release_version.py | 164 +++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 2 deletions(-) diff --git a/scripts/release_version.py b/scripts/release_version.py index 476f394ed1a..5ff179dafb8 100755 --- a/scripts/release_version.py +++ b/scripts/release_version.py @@ -190,8 +190,7 @@ Release candidate schedule: * **{target_year}.{target_month:02d}-rc3**: {rc3_date.strftime('%a %d-%b-%Y')} * **{target_year}.{target_month:02d}-rc2**: {rc2_date.strftime('%a %d-%b-%Y')} * **{target_year}.{target_month:02d}-rc1**: {rc1_date.strftime('%a %d-%b-%Y')} -* **{target_year}.{target_month:02d}** (Final): \\ -{final_date.strftime('%a %d-%b-%Y')} +* **{target_year}.{target_month:02d}** (Final): {final_date.strftime('%a %d-%b-%Y')} ''' return schedule_text diff --git a/test/scripts/test_release_version.py b/test/scripts/test_release_version.py index 14a63beb68c..3758404672d 100755 --- a/test/scripts/test_release_version.py +++ b/test/scripts/test_release_version.py @@ -9,6 +9,7 @@ import io import json import os import shutil +import subprocess import sys import tempfile import unittest @@ -876,6 +877,169 @@ class TestMainFunction(unittest.TestCase): self.assertIn(expected_msg, mock_stdout.getvalue()) +class TestRSTFormatting(unittest.TestCase): + """Test that generated RST content is valid for Sphinx""" + + def setUp(self): + """Set up test fixtures""" + self.test_dir = tempfile.mkdtemp() + + def tearDown(self): + """Clean up test fixtures""" + shutil.rmtree(self.test_dir) + + def test_generate_schedule_rst_validity(self): + """Test that generate_schedule() produces valid RST""" + schedule = generate_schedule() + + # Write schedule to a test file + test_rst_path = os.path.join(self.test_dir, 'test_schedule.rst') + with open(test_rst_path, 'w', encoding='utf-8') as f: + f.write('''.. SPDX-License-Identifier: GPL-2.0+ + +Test Document +============= + +''') + f.write(schedule) + + # Try to validate with rst2html if available (fallback test) + try: + # Run docutils rst2html to validate the RST syntax + result = subprocess.run(['rst2html', test_rst_path], + capture_output=True, text=True, timeout=30) + # If rst2html is available and succeeds, the RST is valid + if result.returncode == 0: + self.assertTrue(True, "RST validation passed with rst2html") + else: + # Check for specific formatting errors + stderr = result.stderr.lower() + if 'bullet list ends without a blank line' in stderr: + self.fail(f"RST formatting error in schedule: {result.stderr}") + elif 'unexpected unindent' in stderr: + self.fail(f"RST formatting error in schedule: {result.stderr}") + except (subprocess.TimeoutExpired, FileNotFoundError): + # rst2html not available or timeout, do basic content checks + pass + + # Basic RST format validation checks + lines = schedule.split('\n') + + # Check for proper bullet list formatting + in_bullet_list = False + last_line_was_bullet = False + + for i, line in enumerate(lines): + stripped = line.strip() + + # Check if this line starts a bullet list item + if stripped.startswith('* '): + in_bullet_list = True + last_line_was_bullet = True + + # Check for line continuation issues + if '\\' in line: + # If there's a backslash continuation, ensure it's properly formatted + # The continuation should be on the same line, not the next line + self.assertFalse(line.rstrip().endswith('\\'), + f"Line {i+1} has improper line continuation: {line}") + elif in_bullet_list and stripped == '': + # Empty line might end the bullet list + last_line_was_bullet = False + elif in_bullet_list and stripped and not stripped.startswith(' '): + # Non-indented content after bullet list should have blank line before it + if last_line_was_bullet: + self.fail(f"Line {i+1} lacks blank line after bullet list: {line}") + in_bullet_list = False + last_line_was_bullet = False + elif in_bullet_list and stripped.startswith(' '): + # Indented content is part of the bullet list + last_line_was_bullet = False + else: + in_bullet_list = False + last_line_was_bullet = False + + def test_update_docs_rst_validity(self): + """Test that update_docs() produces valid RST""" + docs_path = os.path.join(self.test_dir, 'concept_releases.rst') + + # Create initial file + with open(docs_path, 'w', encoding='utf-8') as f: + f.write('''.. SPDX-License-Identifier: GPL-2.0+ + +U-Boot Concept Releases +======================= + +This document tracks all concept releases of U-Boot. + +Release History +--------------- + +''') + + # Add a release that includes schedule generation + release_info = ReleaseInfo( + is_final=False, + version='2025.02-rc1', + year=2025, + month=2, + rc_number=1 + ) + + changes_made = update_docs(release_info, 'abc123def', docs_path) + self.assertTrue(changes_made) + + # Read the updated content + with open(docs_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Try to validate with rst2html if available + try: + result = subprocess.run(['rst2html', docs_path], + capture_output=True, text=True, timeout=30) + if result.returncode != 0: + stderr = result.stderr.lower() + if 'bullet list ends without a blank line' in stderr: + self.fail(f"RST formatting error in generated docs: {result.stderr}") + elif 'unexpected unindent' in stderr: + self.fail(f"RST formatting error in generated docs: {result.stderr}") + except (subprocess.TimeoutExpired, FileNotFoundError): + # rst2html not available, continue with manual checks + pass + + # Manual validation of RST structure + lines = content.split('\n') + + # Find the Next Release section and check its formatting + next_release_idx = -1 + for i, line in enumerate(lines): + if line.strip() == 'Next Release': + next_release_idx = i + break + + if next_release_idx != -1: + # Check the bullet list in the schedule section + in_candidate_schedule = False + for i in range(next_release_idx, len(lines)): + line = lines[i] + if 'Release candidate schedule:' in line: + in_candidate_schedule = True + continue + elif in_candidate_schedule and line.strip().startswith('* '): + # This is a bullet list item + # Check it doesn't have improper line continuation + self.assertFalse(line.rstrip().endswith('\\'), + f"Line {i+1} has improper line continuation in schedule: {line}") + elif in_candidate_schedule and line.strip() == '': + # Empty line - bullet list might be ending + continue + elif (in_candidate_schedule and line.strip() and + not line.strip().startswith('* ') and + not line.startswith(' ')): + # End of bullet list section + break + + class TestReleaseVersionScenarios(unittest.TestCase): """Test realistic release scenarios""" -- 2.43.0

From: Simon Glass <sjg@chromium.org> The test.py tests pass -P to sandbox to tell it to bypass the pager. Tests which change this value must restore it to the old value. Fix this, so that the -P setting remains in place across test runs. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/common/pager.c | 7 ++++--- test/test-main.c | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/common/pager.c b/test/common/pager.c index 1ec27c14527..53902ab29a3 100644 --- a/test/common/pager.c +++ b/test/common/pager.c @@ -403,12 +403,13 @@ static int pager_test_bypass_mode(struct unit_test_state *uts) struct pager *pag; const char *text = "This text should be returned directly"; const char *result; + bool was_bypassed; /* Init with small page length to ensure paging would normally occur */ ut_assertok(pager_init(&pag, 2, 1024)); /* Enable bypass mode */ - pager_set_test_bypass(pag, true); + was_bypassed = pager_set_test_bypass(pag, true); /* Post text - should get original string back directly */ result = pager_post(pag, true, text); @@ -418,8 +419,8 @@ static int pager_test_bypass_mode(struct unit_test_state *uts) result = pager_next(pag, true, 0); ut_assertnull(result); - /* Disable bypass mode */ - pager_set_test_bypass(pag, false); + /* Restore old bypass mode */ + pager_set_test_bypass(pag, was_bypassed); /* Now pager should work normally */ result = pager_post(pag, true, text); diff --git a/test/test-main.c b/test/test-main.c index 77d8a9f689d..b27f892140c 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -708,7 +708,7 @@ int ut_run_list(struct unit_test_state *uts, const char *category, const char *test_insert) { ; - bool has_dm_tests = false; + bool was_bypassed, has_dm_tests = false; ulong start_offset = 0; ulong test_offset = 0; int ret; @@ -748,10 +748,10 @@ int ut_run_list(struct unit_test_state *uts, const char *category, memcpy(uts->fdt_copy, gd->fdt_blob, uts->fdt_size); } uts->force_run = force_run; - pager_set_test_bypass(gd_pager(), true); + was_bypassed = pager_set_test_bypass(gd_pager(), true); ret = ut_run_tests(uts, prefix, tests, count, select_name, test_insert); - pager_set_test_bypass(gd_pager(), false); + pager_set_test_bypass(gd_pager(), was_bypassed); /* Best efforts only...ignore errors */ if (has_dm_tests) -- 2.43.0

From: Simon Glass <sjg@chromium.org> The pager should be bypassed if the terminal was not detected. Fix the condition which actually says the opposite. Also fix a long line in pager_post() Signed-off-by: Simon Glass <sjg@chromium.org> --- common/console.c | 2 +- common/pager.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/console.c b/common/console.c index 77f70588488..561a859dc1e 100644 --- a/common/console.c +++ b/common/console.c @@ -1189,7 +1189,7 @@ static int on_console(const char *name, const char *value, enum env_op op, int lines = calc_check_console_lines(); /* Set bypass mode if not connected to a terminal */ - pager_set_bypass(gd_pager(), lines != 0); + pager_set_bypass(gd_pager(), lines == 0); if (lines) pager_set_page_len(gd_pager(), lines); } diff --git a/common/pager.c b/common/pager.c index 8991d2f93fa..a16937768b6 100644 --- a/common/pager.c +++ b/common/pager.c @@ -20,7 +20,8 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s) struct membuf old; int ret, len; - if (!pag || !use_pager || pag->test_bypass || pag->state == PAGERST_BYPASS) + if (!pag || !use_pager || pag->test_bypass || + pag->state == PAGERST_BYPASS) return s; len = strlen(s); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add support for pressing 'Q' to put the pager into bypass mode,disabling further paging for the current session. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- common/pager.c | 36 +++++++++++++++++++++++++----------- doc/usage/console.rst | 3 +++ include/pager.h | 4 ++++ test/common/pager.c | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/common/pager.c b/common/pager.c index a16937768b6..c0a55b738b9 100644 --- a/common/pager.c +++ b/common/pager.c @@ -71,6 +71,10 @@ const char *pager_next(struct pager *pag, bool use_pager, int key) pag->state = PAGERST_WAIT_USER; return PAGER_PROMPT; case PAGERST_WAIT_USER: + if (key == 'Q') { + pag->state = PAGERST_BYPASS; + return PAGER_BLANK; + } if (key != ' ') return PAGER_WAITING; pag->state = PAGERST_CLEAR_PROMPT; @@ -79,7 +83,7 @@ const char *pager_next(struct pager *pag, bool use_pager, int key) pag->state = PAGERST_OK; break; case PAGERST_BYPASS: - return NULL; + break; } ret = membuf_getraw(&pag->mb, pag->buf.size - 1, false, &str); @@ -93,17 +97,26 @@ const char *pager_next(struct pager *pag, bool use_pager, int key) return NULL; } - /* return lines until we reach the limit */ - for (p = str, end = str + ret; p < end; p++) { - if (*p == '\n' && ++pag->line_count == pag->page_len - 1) { - /* remember to display the pager message next time */ - pag->state = PAGERST_AT_LIMIT; - pag->line_count = 0; - - /* skip the newline, since our prompt has one */ - p++; - break; + end = str + ret; + if (pag->state != PAGERST_BYPASS) { + /* return lines until we reach the limit */ + for (p = str; p < end; p++) { + if (*p == '\n' && + ++pag->line_count == pag->page_len - 1) { + /* + * remember to display the pager message next + * time + */ + pag->state = PAGERST_AT_LIMIT; + pag->line_count = 0; + + /* skip the newline, since our prompt has one */ + p++; + break; + } } + } else { + p = end; } /* remove the used bytes from the membuf */ @@ -178,6 +191,7 @@ static int on_pager(const char *name, const char *value, enum env_op op, if (value) { new_page_len = simple_strtoul(value, NULL, 16); pager_set_page_len(pag, new_page_len); + pager_set_bypass(pag, false); } break; case env_op_delete: diff --git a/doc/usage/console.rst b/doc/usage/console.rst index 4a28ea95906..880a5b7b605 100644 --- a/doc/usage/console.rst +++ b/doc/usage/console.rst @@ -72,6 +72,9 @@ When activated, the pager pauses at the end of each 'page' (screenful) of output, shows a prompt ": Press SPACE to continue" and lets the user read the output. To continue to the next page, press the SPACE key. +The pager can be bypassed by pressing 'Q' at the prompt. This disables the pager +until the 'pager' environment variable is given a new value. + Page Size Configuration ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/include/pager.h b/include/pager.h index b43b332a83c..887f5d1f639 100644 --- a/include/pager.h +++ b/include/pager.h @@ -110,6 +110,10 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s); * busy-wait for a keypress, if desired, since pager_next() will only ever * return PAGER_WAITING until @ch is non-zero. * + * When the pager prompts for user input, pressing SPACE continues to the next + * page, while pressing capital 'Q' puts the pager into bypass mode and + * disables further paging. + * * @pag: Pager to use * @use_pager: Whether or not to use the pager functionality * @ch: Key that the user has pressed, or 0 if none diff --git a/test/common/pager.c b/test/common/pager.c index 53902ab29a3..636069d5b37 100644 --- a/test/common/pager.c +++ b/test/common/pager.c @@ -579,3 +579,42 @@ static int pager_test_console(struct unit_test_state *uts) return 0; } COMMON_TEST(pager_test_console, UTF_CONSOLE); + +/* Test bypass keypress ('Q') functionality */ +static int pager_test_bypass_keypress(struct unit_test_state *uts) +{ + struct pager *pag; + const char *out; + int ret; + + ret = pager_init(&pag, 3, SZ_1K); + ut_assertok(ret); + + /* Post text that will trigger paging */ + out = pager_post(pag, true, "line1\nline2\nline3\nline4\n"); + ut_assertnonnull(out); + ut_asserteq_str("line1\nline2", out); + + /* Should be waiting for user input */ + out = pager_next(pag, true, 0); + ut_asserteq_str(PAGER_PROMPT, out); + + /* Press 'Q' to bypass */ + out = pager_next(pag, true, 'Q'); + ut_asserteq_str(PAGER_BLANK, out); + + /* Verify pager is now in bypass mode */ + ut_asserteq(PAGERST_BYPASS, pag->state); + + /* Next call should return the remaining text without paging */ + out = pager_next(pag, true, 0); + ut_asserteq_str("line3\nline4\n", out); + + /* No more text should be available */ + out = pager_next(pag, true, 0); + ut_asserteq_ptr(NULL, out); + + pager_uninit(pag); + return 0; +} +COMMON_TEST(pager_test_bypass_keypress, 0); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add support for pressing 'q' to throw away any further output until the prompt is reached. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/cli_readline.c | 1 + common/pager.c | 19 +++++++++++++++++ doc/usage/console.rst | 3 ++- include/pager.h | 20 +++++++++++++++++- test/common/pager.c | 47 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 2 deletions(-) diff --git a/common/cli_readline.c b/common/cli_readline.c index dc27a962e9d..244a287b435 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -654,6 +654,7 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer, static int initted; bool old_bypass; + pager_clear_quit(gd_pager()); old_bypass = pager_set_bypass(gd_pager(), true); /* diff --git a/common/pager.c b/common/pager.c index c0a55b738b9..60b1adc8571 100644 --- a/common/pager.c +++ b/common/pager.c @@ -24,6 +24,9 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s) pag->state == PAGERST_BYPASS) return s; + if (pag->state == PAGERST_QUIT_SUPPRESS) + return NULL; + len = strlen(s); if (!len) return NULL; @@ -75,6 +78,10 @@ const char *pager_next(struct pager *pag, bool use_pager, int key) pag->state = PAGERST_BYPASS; return PAGER_BLANK; } + if (key == 'q') { + pag->state = PAGERST_QUIT_SUPPRESS; + return "\r \r"; + } if (key != ' ') return PAGER_WAITING; pag->state = PAGERST_CLEAR_PROMPT; @@ -84,6 +91,9 @@ const char *pager_next(struct pager *pag, bool use_pager, int key) break; case PAGERST_BYPASS: break; + case PAGERST_QUIT_SUPPRESS: + membuf_purge(&pag->mb); + return NULL; } ret = membuf_getraw(&pag->mb, pag->buf.size - 1, false, &str); @@ -176,6 +186,15 @@ void pager_reset(struct pager *pag) pag->line_count = 0; } +void pager_clear_quit(struct pager *pag) +{ + if (!pag) + return; + + if (pag->state == PAGERST_QUIT_SUPPRESS) + pag->state = PAGERST_OK; +} + static int on_pager(const char *name, const char *value, enum env_op op, int flags) { diff --git a/doc/usage/console.rst b/doc/usage/console.rst index 880a5b7b605..088760c6f86 100644 --- a/doc/usage/console.rst +++ b/doc/usage/console.rst @@ -70,7 +70,8 @@ only available if `CONFIG_CONSOLE_MUX` is also enabled. When activated, the pager pauses at the end of each 'page' (screenful) of output, shows a prompt ": Press SPACE to continue" and lets the user read the -output. To continue to the next page, press the SPACE key. +output. To continue to the next page, press the SPACE key. To quit paging +without seeing further output from the current command, press 'q'. The pager can be bypassed by pressing 'Q' at the prompt. This disables the pager until the 'pager' environment variable is given a new value. diff --git a/include/pager.h b/include/pager.h index 887f5d1f639..3f13f82885d 100644 --- a/include/pager.h +++ b/include/pager.h @@ -33,6 +33,7 @@ * @PAGERST_WAIT_USER: Waiting for the user to press a key * @PAGERST_CLEAR_PROMPT: Clearing the prompt ready for more output * @PAGERST_BYPASS: Pager is being bypassed + * @PAGERST_QUIT_SUPPRESS: Output is being suppressed after 'q' keypress */ enum pager_state { PAGERST_OK, @@ -40,6 +41,7 @@ enum pager_state { PAGERST_WAIT_USER, PAGERST_CLEAR_PROMPT, PAGERST_BYPASS, + PAGERST_QUIT_SUPPRESS, }; /** @@ -112,7 +114,8 @@ const char *pager_post(struct pager *pag, bool use_pager, const char *s); * * When the pager prompts for user input, pressing SPACE continues to the next * page, while pressing capital 'Q' puts the pager into bypass mode and - * disables further paging. + * disables further paging. Pressing 'q' quits and suppresses all output until + * the next command prompt. * * @pag: Pager to use * @use_pager: Whether or not to use the pager functionality @@ -157,6 +160,17 @@ bool pager_set_test_bypass(struct pager *pag, bool bypass); */ void pager_reset(struct pager *pag); +/** + * pager_clear_quit() - Clear quit suppression mode + * + * If the pager is in PAGERST_QUIT_SUPPRESS state, this resets it to normal + * operation (PAGERST_OK). This is typically called at the start of + * cli_readline_into_buffer() to allow new commands to display output normally. + * + * @pag: Pager to update, may be NULL in which case this function does nothing + */ +void pager_clear_quit(struct pager *pag); + /** * pager_uninit() - Uninit the pager * @@ -188,6 +202,10 @@ static inline bool pager_set_test_bypass(struct pager *pag, bool bypass) return true; } +static inline void pager_clear_quit(struct pager *pag) +{ +} + static inline void pager_reset(struct pager *pag) { } diff --git a/test/common/pager.c b/test/common/pager.c index 636069d5b37..ccd5230f3a4 100644 --- a/test/common/pager.c +++ b/test/common/pager.c @@ -618,3 +618,50 @@ static int pager_test_bypass_keypress(struct unit_test_state *uts) return 0; } COMMON_TEST(pager_test_bypass_keypress, 0); + +/* Test quit keypress ('q') functionality */ +static int pager_test_quit_keypress(struct unit_test_state *uts) +{ + struct pager *pag; + const char *out; + int ret; + + ret = pager_init(&pag, 3, SZ_1K); + ut_assertok(ret); + + /* Post text that will trigger paging */ + out = pager_post(pag, true, "line1\nline2\nline3\nline4\n"); + ut_assertnonnull(out); + ut_asserteq_str("line1\nline2", out); + + /* Should be waiting for user input */ + out = pager_next(pag, true, 0); + ut_asserteq_str(PAGER_PROMPT, out); + + /* Press 'q' to quit and suppress */ + out = pager_next(pag, true, 'q'); + ut_asserteq_str("\r \r", out); + + /* Verify pager is now in quit suppress mode */ + ut_asserteq(PAGERST_QUIT_SUPPRESS, pag->state); + + /* Next call should return NULL (suppressed) */ + out = pager_next(pag, true, 0); + ut_asserteq_ptr(NULL, out); + + /* Posting new text should also return NULL (suppressed) */ + out = pager_post(pag, true, "new text\n"); + ut_asserteq_ptr(NULL, out); + + /* Test that pager_clear_quit() restores normal operation */ + pager_clear_quit(pag); + ut_asserteq(PAGERST_OK, pag->state); + + /* and that any new test appears */ + out = pager_post(pag, true, "more new text\n"); + ut_asserteq_str("more new text\n", out); + + pager_uninit(pag); + return 0; +} +COMMON_TEST(pager_test_quit_keypress, 0); -- 2.43.0

From: Simon Glass <sjg@chromium.org> This feature was designed for tracing but can be useful for debugging too, since it is possible to examine the state of the system just before handing off to the OS. Provide a separate CONFIG_BOOTM_FAKE_GO option to allow this feature to be used separate from tracing. Enable it for the EFI app. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/Kconfig | 8 ++++++++ boot/bootm.c | 5 ++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/boot/Kconfig b/boot/Kconfig index 13036fd3a09..8f0cecfb1f6 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -453,6 +453,14 @@ config BOOTM_VXWORKS help Support booting VxWorks images via the bootm command. +config BOOTM_FAKE_GO + bool "Support faking a boot, for debugging purposes" + default y if TRACE || EFI_APP + help + Provides a way to fake the boot, so that almost everything is done + to prepare for the boot, but the jump to the OS does not actually + happen. This can be useful for debugging. + endif # BOOT config PXE_UTILS diff --git a/boot/bootm.c b/boot/bootm.c index a536737db09..0981494a2b8 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -1260,9 +1260,9 @@ int bootm_run_states(struct bootm_info *bmi, int states) ret = boot_fn(BOOTM_STATE_OS_PREP, bmi); } -#ifdef CONFIG_TRACE /* Pretend to run the OS, then run a user command */ - if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + if (IS_ENABLED(CONFIG_BOOTM_FAKE_GO) && !ret && + (states & BOOTM_STATE_OS_FAKE_GO)) { char *cmd_list = env_get("fakegocmd"); log_debug("fake_go\n"); @@ -1270,7 +1270,6 @@ int bootm_run_states(struct bootm_info *bmi, int states) if (!ret && cmd_list) ret = run_command_list(cmd_list, -1, 0); } -#endif /* Check for unsupported subcommand. */ if (ret) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than always going through this state, require callers to explicitly request it. This will allow the option to be enabled without affecting the boot, unless the user expressly requests it. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 9 ++++----- doc/usage/cmd/bootm.rst | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index 0981494a2b8..d86fedc7be1 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -1300,8 +1300,7 @@ int boot_run(struct bootm_info *bmi, const char *cmd, int extra_states) int states; bmi->cmd_name = cmd; - states = BOOTM_STATE_MEASURE | BOOTM_STATE_OS_PREP | - BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO; + states = BOOTM_STATE_MEASURE | BOOTM_STATE_OS_PREP; if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH)) states |= BOOTM_STATE_RAMDISK; states |= extra_states; @@ -1318,7 +1317,7 @@ int bootm_run(struct bootm_info *bmi) { return boot_run(bmi, "bootm", BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | - BOOTM_STATE_LOADOS); + BOOTM_STATE_LOADOS | BOOTM_STATE_OS_GO); } int bootz_run(struct bootm_info *bmi) @@ -1356,7 +1355,7 @@ int bootz_run(struct bootm_info *bmi) images->os.os = IH_OS_LINUX; - return boot_run(bmi, "bootz", 0); + return boot_run(bmi, "bootz", BOOTM_STATE_OS_GO); } int booti_run(struct bootm_info *bmi) @@ -1365,7 +1364,7 @@ int booti_run(struct bootm_info *bmi) return boot_run(bmi, "booti", BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | - BOOTM_STATE_LOADOS); + BOOTM_STATE_LOADOS | BOOTM_STATE_OS_GO); } void bootm_read_env(struct bootm_info *bmi) diff --git a/doc/usage/cmd/bootm.rst b/doc/usage/cmd/bootm.rst index 5af331873ce..dde441d8360 100644 --- a/doc/usage/cmd/bootm.rst +++ b/doc/usage/cmd/bootm.rst @@ -130,7 +130,8 @@ prep are required. After this the OS itself is ready to boot. fake - This is only used for testing and only available when `CONFIG_TRACE` is + This is only used for testing and only available when `CONFIG_BOOTM_FAKE_GO` + is enabled. It fakes a boot of the OS, performs all the normal steps right up to the point where U-Boot is about to jump to the OS. It then runs a list of commands from the `fakegocmd` environment variable. Note that the -- 2.43.0

From: Simon Glass <sjg@chromium.org> Use a BIT() macro to specify the flags. Fix an extra space in the comment while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/bootflow.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/bootflow.h b/include/bootflow.h index 422fd32a3ca..c8f5d6e0859 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -42,18 +42,21 @@ enum bootflow_state_t { /** * enum bootflow_flags_t - flags for bootflows * + * These flags are set up by the bootmeth when the bootflow is created, except + * for BOOTFLOWF_FAKE which can be set later. + * * @BOOTFLOWF_USE_PRIOR_FDT: Indicates that an FDT was not found by the bootmeth * and it is using the prior-stage FDT, which is the U-Boot control FDT. * This is only possible with the EFI bootmeth (distro-efi) and only when * CONFIG_OF_HAS_PRIOR_STAGE is enabled * @BOOTFLOWF_STATIC_BUF: Indicates that @bflow->buf is statically set, rather * than being allocated by malloc(). - * @BOOTFLOWF_USE_BUILTIN_FDT : Indicates that current bootflow uses built-in FDT + * @BOOTFLOWF_USE_BUILTIN_FDT: Indicates that current bootflow uses built-in FDT */ enum bootflow_flags_t { - BOOTFLOWF_USE_PRIOR_FDT = 1 << 0, - BOOTFLOWF_STATIC_BUF = 1 << 1, - BOOTFLOWF_USE_BUILTIN_FDT = 1 << 2, + BOOTFLOWF_USE_PRIOR_FDT = BIT(0), + BOOTFLOWF_STATIC_BUF = BIT(1), + BOOTFLOWF_USE_BUILTIN_FDT = BIT(2), }; /** -- 2.43.0

From: Simon Glass <sjg@chromium.org> Allow using 'bootflow boot -f' to fake a boot. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/bootflow.c | 16 ++++++++++++++-- doc/usage/cmd/bootflow.rst | 3 +++ include/bootflow.h | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 43335fecb34..33ed9a1cd73 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -523,8 +523,12 @@ static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc, { struct bootstd_priv *std; struct bootflow *bflow; + bool fake = false; int ret; + if (IS_ENABLED(CONFIG_BOOTM_FAKE_GO) && argc > 1 && *argv[1] == '-') + fake = strchr(argv[1], 'f'); + ret = bootstd_get_priv(&std); if (ret) return CMD_RET_FAILURE; @@ -538,6 +542,14 @@ static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc, return CMD_RET_FAILURE; } bflow = std->cur_bootflow; + + if (IS_ENABLED(CONFIG_BOOTM_FAKE_GO)) { + if (fake) + bflow->flags |= BOOTFLOWF_FAKE_GO; + else + bflow->flags &= ~BOOTFLOWF_FAKE_GO; + } + log_debug("cmd bflow flags %x\n", bflow->flags); ret = bootflow_run_boot(NULL, bflow); if (ret) return CMD_RET_FAILURE; @@ -649,7 +661,7 @@ U_BOOT_LONGHELP(bootflow, "bootflow select [<num>|<name>] - select a bootflow\n" "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n" "bootflow read - read all current-bootflow files\n" - "bootflow boot - boot current bootflow\n" + "bootflow boot [-f] - boot current bootflow (-f fake)\n" "bootflow menu [-t] - show a menu of available bootflows\n" "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline" #else @@ -664,7 +676,7 @@ U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text, U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select), U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info), U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read), - U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot), + U_BOOT_SUBCMD_MKENT(boot, 2, 1, do_bootflow_boot), U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu), U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline), #endif diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index d57a6e36c3b..938e5c79903 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -231,6 +231,9 @@ bootflow boot This boots the current bootflow, reading any required files first. +For debugging only, the `-f` flag can be provided to cause a fake boot. This +runs the boot through to the point of handing off to the OS, then returns, to +allow the state to be examined. bootflow cmdline ~~~~~~~~~~~~~~~~ diff --git a/include/bootflow.h b/include/bootflow.h index c8f5d6e0859..284c23c59dd 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -52,11 +52,14 @@ enum bootflow_state_t { * @BOOTFLOWF_STATIC_BUF: Indicates that @bflow->buf is statically set, rather * than being allocated by malloc(). * @BOOTFLOWF_USE_BUILTIN_FDT: Indicates that current bootflow uses built-in FDT + * @BOOTFLOWF_FAKE_GO: Do a 'fake' boot, up to the last possible point, then + * return */ enum bootflow_flags_t { BOOTFLOWF_USE_PRIOR_FDT = BIT(0), BOOTFLOWF_STATIC_BUF = BIT(1), BOOTFLOWF_USE_BUILTIN_FDT = BIT(2), + BOOTFLOWF_FAKE_GO = BIT(3), }; /** -- 2.43.0

From: Simon Glass <sjg@chromium.org> This message includes the 0x prefix which is not used elsewhere. Drop it for consistency. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootm.c | 2 +- cmd/booti.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boot/bootm.c b/boot/bootm.c index d86fedc7be1..42259256ad4 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -848,7 +848,7 @@ static int bootm_load_os(struct bootm_info *bmi, int boot_progress) /* Handle BOOTM_STATE_LOADOS */ if (relocated_addr != load) { - printf("Moving Image from 0x%lx to 0x%lx, end=0x%lx\n", + printf("Moving Image from %lx to %lx, end %lx\n", load, relocated_addr, relocated_addr + image_size); memmove((void *)relocated_addr, load_buf, image_size); diff --git a/cmd/booti.c b/cmd/booti.c index bfbe530cb15..017cb6ed923 100644 --- a/cmd/booti.c +++ b/cmd/booti.c @@ -78,7 +78,7 @@ static int booti_start(struct bootm_info *bmi) /* Handle BOOTM_STATE_LOADOS */ if (relocated_addr != ld) { - printf("Moving Image from 0x%lx to 0x%lx, end=0x%lx\n", ld, + printf("Moving Image from %lx to %lx, end %lx\n", ld, relocated_addr, relocated_addr + image_size); memmove((void *)relocated_addr, (void *)ld, image_size); } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add a new debug line for the offset of the ramdisk node. Fix up some missing newlines on other log messages in this file, while we are here. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-board.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/boot/image-board.c b/boot/image-board.c index 9d75def96e2..8dbc344ffb9 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -392,7 +392,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a break; case IMAGE_FORMAT_FIT: if (CONFIG_IS_ENABLED(FIT)) { - log_debug("ramdisk fit load"); + log_debug("ramdisk fit load\n"); rd_noffset = fit_image_load(images, rd_addr, &fit_uname_ramdisk, &fit_uname_config, @@ -400,6 +400,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a BOOTSTAGE_ID_FIT_RD_START, FIT_LOAD_OPTIONAL_NON_ZERO, rd_datap, rd_lenp); + log_debug("rd_noffset %d\n", rd_noffset); if (rd_noffset < 0) return rd_noffset; @@ -418,7 +419,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a void *vendor_boot_img = map_sysmem(get_avendor_bootimg_addr(), 0); void *ramdisk_img; - log_debug("android abootimg"); + log_debug("android abootimg\n"); if (init_boot_img == -1) ramdisk_img = map_sysmem(boot_img, 0); else @@ -431,7 +432,7 @@ static int select_ramdisk(struct bootm_headers *images, const char *select, u8 a } else { void *ptr = map_sysmem(images->os.start, 0); - log_debug("android other"); + log_debug("android other\n"); ret = android_image_get_ramdisk(ptr, NULL, rd_datap, rd_lenp); unmap_sysmem(ptr); } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide a way to pass the 'fake go' flag from the bootflow flag through to the PXE implementation, so that a request for a fake go (via 'bootflow boot -f') is handled correctly in the bootmeth and when booting. Add a little more debugging of this in PXE. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/ext_pxe_common.c | 4 ++++ boot/pxe_utils.c | 21 ++++++++++++++++++--- include/pxe_utils.h | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/boot/ext_pxe_common.c b/boot/ext_pxe_common.c index 67d7b68d310..3b1412b86d3 100644 --- a/boot/ext_pxe_common.c +++ b/boot/ext_pxe_common.c @@ -89,6 +89,9 @@ static int extlinux_setup(struct udevice *dev, struct bootflow *bflow, false, plat->use_fallback, bflow); if (ret) return log_msg_ret("ctx", ret); + log_debug("bootfl flags %x\n", bflow->flags); + if (bflow->flags & BOOTFLOWF_FAKE_GO) + ctx->fake_go = true; return 0; } @@ -103,6 +106,7 @@ int extlinux_boot(struct udevice *dev, struct bootflow *bflow, /* if we have already selected a label, just boot it */ if (plat->ctx.label) { + plat->ctx.fake_go = bflow->flags & BOOTFLOWF_FAKE_GO; ret = pxe_do_boot(&plat->ctx); } else { ret = extlinux_setup(dev, bflow, getfile, allow_abs_path, diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 13fd815451d..981023a3012 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -427,6 +427,11 @@ skip_overlay: static int label_process_fdt(struct pxe_context *ctx, struct pxe_label *label, char *kernel_addr, const char **fdt_argp) { + log_debug("label '%s' kernel_addr '%s' label->fdt '%s' fdtdir '%s' " + "kernel_label '%s' fdt_argp '%s'\n", + label->name, kernel_addr, label->fdt, label->fdtdir, + label->kernel_label, *fdt_argp); + /* For FIT, the label can be identical to kernel one */ if (label->fdt && !strcmp(label->kernel_label, label->fdt)) { *fdt_argp = kernel_addr; @@ -594,7 +599,11 @@ static int label_run_boot(struct pxe_context *ctx, struct pxe_label *label, int states; states = ctx->restart ? BOOTM_STATE_RESTART : BOOTM_STATE_START; - log_debug("using bootm\n"); + log_debug("using bootm fake_go=%d\n", ctx->fake_go); + if (ctx->fake_go) + states |= BOOTM_STATE_OS_FAKE_GO; + else + states |= BOOTM_STATE_OS_GO; ret = boot_run(&bmi, "ext", states | BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS); @@ -782,8 +791,10 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) if (!conf_fdt_str) { if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) || - strcmp("-", label->fdt)) + strcmp("-", label->fdt)) { conf_fdt_str = env_get("fdt_addr"); + log_debug("using fdt_addr '%s'\n", conf_fdt_str); + } } if (!conf_fdt_str) { @@ -792,13 +803,17 @@ static int label_boot(struct pxe_context *ctx, struct pxe_label *label) buf = map_sysmem(kern_addr, 0); if (genimg_get_format(buf) != IMAGE_FORMAT_FIT) { if (!IS_ENABLED(CONFIG_SUPPORT_PASSING_ATAGS) || - strcmp("-", label->fdt)) + strcmp("-", label->fdt)) { conf_fdt_str = env_get("fdtcontroladdr"); + log_debug("using fdtcontroladdr '%s'\n", + conf_fdt_str); + } } unmap_sysmem(buf); } if (conf_fdt_str) conf_fdt = hextoul(conf_fdt_str, NULL); + log_debug("conf_fdt %lx\n", conf_fdt); if (ctx->bflow && conf_fdt_str) ctx->bflow->fdt_addr = conf_fdt; diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 9100a861ba1..7ecb5788d0b 100644 --- a/include/pxe_utils.h +++ b/include/pxe_utils.h @@ -135,6 +135,7 @@ typedef int (*pxe_getfile_func)(struct pxe_context *ctx, const char *file_path, * @conf_fdt: FDT address * @restart: true to use BOOTM_STATE_RESTART instead of BOOTM_STATE_START (only * supported with FIT / bootm) + * @fake_go: Do a 'fake' boot, up to the last possible point, then return */ struct pxe_context { /** @@ -170,6 +171,7 @@ struct pxe_context { char *conf_fdt_str; ulong conf_fdt; bool restart; + bool fake_go; }; /** -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide some debugging for the select string and the FDT which is actually chosen from the FIT. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-fdt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 87c1fef2193..e692fd1f058 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -170,6 +170,7 @@ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size) return 0; if (fdt_check_header(fdt_blob) != 0) { + log_debug("fdt at %lx\n", (ulong)map_to_sysmem(fdt_blob)); fdt_error("image is not a fdt"); goto error; } @@ -330,6 +331,7 @@ static int select_fdt(struct bootm_headers *images, const char *select, u8 arch, /* use FIT configuration provided in first bootm * command argument */ + log_debug("using existing config node\n"); fdt_addr = map_to_sysmem(images->fit_hdr_os); fdt_noffset = fit_get_node_from_config(images, FIT_FDT_PROP, fdt_addr); @@ -446,6 +448,7 @@ int boot_get_fdt(void *buf, const char *select, uint arch, char *fdt_blob = NULL; ulong fdt_addr; + log_debug("select '%s'\n", select); if (select || genimg_has_config(images)) { int ret; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Drop an unwanted blank line and format a few lines to 80cols. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-fit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boot/image-fit.c b/boot/image-fit.c index 0c1c07c8498..617bc3664d9 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -2505,18 +2505,18 @@ int fit_image_load(struct bootm_headers *images, ulong addr, prop_name, ph_type, bootstage_id, &fit_base_uname_config); if (noffset >= 0) { - ret = check_allowed(fit, noffset, images, image_type, arch, bootstage_id); if (ret) return ret; - ret = obtain_data(fit, noffset, prop_name, bootstage_id, &buf, &len); + ret = obtain_data(fit, noffset, prop_name, bootstage_id, &buf, + &len); if (ret) return ret; - ret = handle_load_op(fit, noffset, prop_name, buf, len, image_type, - load_op, bootstage_id, &load); + ret = handle_load_op(fit, noffset, prop_name, buf, len, + image_type, load_op, bootstage_id, &load); if (ret) return ret; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide return values from a few functions to make it easier to track the flow of execution in a few places. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/image-fit.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boot/image-fit.c b/boot/image-fit.c index 617bc3664d9..7bcdb97985a 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -2467,10 +2467,12 @@ static int handle_load_op(const void *fit, int noffset, const char *prop_name, prop_name, data, load); } else { load = data; /* No load address specified */ + log_debug("No load\n"); } ret = decomp_image(fit, noffset, prop_name, buf, size, image_type, load_op, bootstage_id, data, load, load_end); + log_debug("Decomp ret %d\n", ret); if (ret) return ret; @@ -2517,6 +2519,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr, ret = handle_load_op(fit, noffset, prop_name, buf, len, image_type, load_op, bootstage_id, &load); + log_debug("handle_load_op() returned %d\n", ret); if (ret) return ret; @@ -2527,6 +2530,7 @@ int fit_image_load(struct bootm_headers *images, ulong addr, } /* note that fit_uname will always be NULL if noffset == -ENOPKG */ + log_debug("noffset %d\n", noffset); if (noffset >= 0 || noffset == -ENOPKG) { if (fit_unamep) *fit_unamep = (char *)fit_uname; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than panicing, return the error so that the top-level bootm logic can decide what to do. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/arm/lib/bootm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 02ff8beaeab..7c650c3ab67 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -164,14 +164,15 @@ static void do_nonsec_virt_switch(void) __weak void board_prep_linux(struct bootm_headers *images) { } /* Subcommand: PREP */ -static void boot_prep_linux(struct bootm_headers *images) +static int boot_prep_linux(struct bootm_headers *images) { char *commandline = env_get("bootargs"); if (CONFIG_IS_ENABLED(OF_LIBFDT) && IS_ENABLED(CONFIG_LMB) && images->ft_len) { debug("using: FDT\n"); if (image_setup_linux(images)) { - panic("FDT creation failed!"); + log_err("FDT creation failed!"); + return -EINVAL; } } else if (BOOTM_ENABLE_TAGS) { debug("using: ATAGS\n"); @@ -207,6 +208,8 @@ static void boot_prep_linux(struct bootm_headers *images) } board_prep_linux(images); + + return 0; } __weak bool armv7_boot_nonsec_default(void) @@ -345,6 +348,7 @@ static void boot_jump_linux(struct bootm_headers *images, int flag) int do_bootm_linux(int flag, struct bootm_info *bmi) { struct bootm_headers *images = bmi->images; + int ret; log_debug("boot linux flag %x\n", flag); /* No need for those on ARM */ @@ -353,7 +357,9 @@ int do_bootm_linux(int flag, struct bootm_info *bmi) if (flag & BOOTM_STATE_OS_PREP) { log_debug("Preparing to boot Linux\n"); - boot_prep_linux(images); + ret = boot_prep_linux(images); + if (ret) + return ret; return 0; } @@ -364,7 +370,9 @@ int do_bootm_linux(int flag, struct bootm_info *bmi) } log_debug("No flags set: continuing to prepare and jump to Linux\n"); - boot_prep_linux(images); + ret = boot_prep_linux(images); + if (ret) + return ret; boot_jump_linux(images, flag); return 0; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> When an FDT is not provided, U-Boot currently says: FDT and ATAGS support not compiled in even if CONFIG_OF_LIBFDT and CONFIG_LMB are enabled. This can send people on a wild goose chase. Add a separate message for a devicetree not being present. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/arm/lib/bootm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 7c650c3ab67..111c2132c6e 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -167,8 +167,9 @@ __weak void board_prep_linux(struct bootm_headers *images) { } static int boot_prep_linux(struct bootm_headers *images) { char *commandline = env_get("bootargs"); + bool use_fdt = CONFIG_IS_ENABLED(OF_LIBFDT) && IS_ENABLED(CONFIG_LMB); - if (CONFIG_IS_ENABLED(OF_LIBFDT) && IS_ENABLED(CONFIG_LMB) && images->ft_len) { + if (use_fdt && images->ft_len) { debug("using: FDT\n"); if (image_setup_linux(images)) { log_err("FDT creation failed!"); @@ -203,6 +204,9 @@ static int boot_prep_linux(struct bootm_headers *images) } setup_board_tags(¶ms); setup_end_tag(gd->bd); + } else if (use_fdt && !images->ft_len) { + log_err("No FDT provided\n"); + return -ENOENT; } else { panic("FDT and ATAGS support not compiled in\n"); } -- 2.43.0

From: Simon Glass <sjg@chromium.org> When building xilinx_zynqmp_kria we cannot access lmb_alloc() since it it is not present in the image. The linker garbage-collection does not seem to handle this either. Follow the normal procedure in this case and declare a static inline. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/lmb.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/lmb.h b/include/lmb.h index f221f0cce8f..3b52ee33abe 100644 --- a/include/lmb.h +++ b/include/lmb.h @@ -93,7 +93,16 @@ long lmb_reserve(phys_addr_t base, phys_size_t size); */ long lmb_reserve_flags(phys_addr_t base, phys_size_t size, enum lmb_flags flags); + +#if CONFIG_IS_ENABLED(LMB) phys_addr_t lmb_alloc(phys_size_t size, ulong align); +#else +static inline phys_addr_t lmb_alloc(phys_size_t size, ulong align) +{ + return 0; +} +#endif + phys_addr_t lmb_alloc_base(phys_size_t size, ulong align, phys_addr_t max_addr); phys_addr_t lmb_alloc_addr(phys_addr_t base, phys_size_t size); phys_size_t lmb_get_free_size(phys_addr_t addr); -- 2.43.0

From: Simon Glass <sjg@chromium.org> It isn't necessarily safe to move the kernel (up to) 2MB higher in memory. If the kernel is in a FIT, this may overwrite other data such as the devicetree. Use lmb (where available) as is done with other image relocations. Update the return value to provide more specific information on failure. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/arm/lib/image.c | 15 +++++++++++---- include/image.h | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/arm/lib/image.c b/arch/arm/lib/image.c index d78d704cb58..f97276a84c5 100644 --- a/arch/arm/lib/image.c +++ b/arch/arm/lib/image.c @@ -48,7 +48,7 @@ int booti_setup(ulong image, ulong *relocated_addr, ulong *size, if (!booti_is_valid(ih)) { puts("Bad Linux ARM64 Image magic!\n"); - return 1; + return -EPERM; } /* @@ -73,10 +73,17 @@ int booti_setup(ulong image, ulong *relocated_addr, ulong *size, * images->ep. Otherwise, relocate the image to the base of RAM * since memory below it is not accessible via the linear mapping. */ - if (!force_reloc && (le64_to_cpu(ih->flags) & BIT(3))) - dst = image - text_offset; - else + if (!force_reloc && (le64_to_cpu(ih->flags) & BIT(3))) { + if (IS_ENABLED(CONFIG_LMB)) { + dst = lmb_alloc(image_size, SZ_2M); + if (!dst) + return -ENOSPC; + } else { + dst = image - text_offset; + } + } else { dst = gd->bd->bi_dram[0].start; + } *relocated_addr = ALIGN(dst, SZ_2M) + text_offset; diff --git a/include/image.h b/include/image.h index 7f3ca1088de..a972e3f6921 100644 --- a/include/image.h +++ b/include/image.h @@ -1134,7 +1134,8 @@ int bootz_setup(ulong image, ulong *start, ulong *end); * @start: Returns start address of image * @size : Returns size image * @force_reloc: Ignore image->ep field, always place image to RAM start - * Return: 0 if OK, 1 if the image was not recognised + * Return: 0 if OK, -EPERM image was not recognised, -ENOSPC if there was not + * enough lmb space */ int booti_setup(ulong image, ulong *relocated_addr, ulong *size, bool force_reloc); -- 2.43.0

From: Simon Glass <sjg@chromium.org> This header makes use of efi_status_t so should include the header which provides it. Add this. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi_variable.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/efi_variable.h b/include/efi_variable.h index 4065cf45eca..4c9e0149b39 100644 --- a/include/efi_variable.h +++ b/include/efi_variable.h @@ -6,6 +6,7 @@ #ifndef _EFI_VARIABLE_H #define _EFI_VARIABLE_H +#include <efi.h> #include <linux/bitops.h> #define EFI_VARIABLE_READ_ONLY 0x80000000 -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move the efi_exit() function further up the file and call it if the board_init_r() function returns. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi_client/efi_app.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c index 25fc2e9cade..b99de578fac 100644 --- a/lib/efi_client/efi_app.c +++ b/lib/efi_client/efi_app.c @@ -185,6 +185,14 @@ static void find_protocols(struct efi_priv *priv) boot->locate_protocol(&guid, NULL, (void **)&priv->efi_dp_to_text); } +static void efi_exit(void) +{ + struct efi_priv *priv = efi_get_priv(); + + printf("U-Boot EFI exiting\n"); + priv->boot->exit(priv->parent_image, EFI_SUCCESS, 0, NULL); +} + /** * efi_main() - Start an EFI image * @@ -237,18 +245,11 @@ efi_status_t EFIAPI efi_main(efi_handle_t image, gd = gd->new_gd; board_init_r(NULL, 0); free_memory(priv); + efi_exit(); return EFI_SUCCESS; } -static void efi_exit(void) -{ - struct efi_priv *priv = efi_get_priv(); - - printf("U-Boot EFI exiting\n"); - priv->boot->exit(priv->parent_image, EFI_SUCCESS, 0, NULL); -} - static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type) { struct efi_priv *priv = efi_get_priv(); -- 2.43.0

From: Simon Glass <sjg@chromium.org> It is common for the main program to be at the bottom, but with recent changes it has crept up a bit. Move it back to the bottom. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi_client/efi_app.c | 114 +++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c index b99de578fac..c851173f1ae 100644 --- a/lib/efi_client/efi_app.c +++ b/lib/efi_client/efi_app.c @@ -193,63 +193,6 @@ static void efi_exit(void) priv->boot->exit(priv->parent_image, EFI_SUCCESS, 0, NULL); } -/** - * efi_main() - Start an EFI image - * - * This function is called by our EFI start-up code. It handles running - * U-Boot. If it returns, EFI will continue. Another way to get back to EFI - * is via reset_cpu(). - */ -efi_status_t EFIAPI efi_main(efi_handle_t image, - struct efi_system_table *sys_table) -{ - struct efi_priv local_priv, *priv = &local_priv; - efi_status_t ret; - - /* Set up access to EFI data structures */ - ret = efi_init(priv, "App", image, sys_table); - if (ret) { - printf("Failed to set up U-Boot: err=%lx\n", ret); - return ret; - } - efi_set_priv(priv); - - /* - * Set up the EFI debug UART so that printf() works. This is - * implemented in the EFI serial driver, serial_efi.c. The application - * can use printf() freely. - */ - debug_uart_init(); - - ret = setup_memory(priv); - if (ret) { - printf("Failed to set up memory: ret=%lx\n", ret); - return ret; - } - - scan_tables(priv->sys_table); - find_protocols(priv); - - /* - * We could store the EFI memory map here, but it changes all the time, - * so this is only useful for debugging. - * - * ret = efi_store_memory_map(priv); - * if (ret) - * return ret; - */ - - printf("starting\n"); - - board_init_f(GD_FLG_SKIP_RELOC); - gd = gd->new_gd; - board_init_r(NULL, 0); - free_memory(priv); - efi_exit(); - - return EFI_SUCCESS; -} - static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type) { struct efi_priv *priv = efi_get_priv(); @@ -421,3 +364,60 @@ U_BOOT_DRIVER(efi_sysreset) = { .of_match = efi_sysreset_ids, .ops = &efi_sysreset_ops, }; + +/** + * efi_main() - Start an EFI image + * + * This function is called by our EFI start-up code. It handles running + * U-Boot. If it returns, EFI will continue. Another way to get back to EFI + * is via reset_cpu(). + */ +efi_status_t EFIAPI efi_main(efi_handle_t image, + struct efi_system_table *sys_table) +{ + struct efi_priv local_priv, *priv = &local_priv; + efi_status_t ret; + + /* Set up access to EFI data structures */ + ret = efi_init(priv, "App", image, sys_table); + if (ret) { + printf("Failed to set up U-Boot: err=%lx\n", ret); + return ret; + } + efi_set_priv(priv); + + /* + * Set up the EFI debug UART so that printf() works. This is + * implemented in the EFI serial driver, serial_efi.c. The application + * can use printf() freely. + */ + debug_uart_init(); + + ret = setup_memory(priv); + if (ret) { + printf("Failed to set up memory: ret=%lx\n", ret); + return ret; + } + + scan_tables(priv->sys_table); + find_protocols(priv); + + /* + * We could store the EFI memory map here, but it changes all the time, + * so this is only useful for debugging. + * + * ret = efi_store_memory_map(priv); + * if (ret) + * return ret; + */ + + printf("starting\n"); + + board_init_f(GD_FLG_SKIP_RELOC); + gd = gd->new_gd; + board_init_r(NULL, 0); + free_memory(priv); + efi_exit(); + + return EFI_SUCCESS; +} -- 2.43.0

From: Simon Glass <sjg@chromium.org> Enable some options which are useful for development: - Show function names when logging - Enable SMBIOS so we can view these tables - Include kaslrseed support - Enable commands for hashing images - Support write on FAT filesystems Signed-off-by: Simon Glass <sjg@chromium.org> --- configs/efi-arm_app64_defconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configs/efi-arm_app64_defconfig b/configs/efi-arm_app64_defconfig index 2bc7a87506d..8defb83f9d4 100644 --- a/configs/efi-arm_app64_defconfig +++ b/configs/efi-arm_app64_defconfig @@ -21,9 +21,11 @@ CONFIG_BOOTCOMMAND="bootflow scan -lbp" CONFIG_SYS_PBSIZE=532 CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_LOG=y +CONFIG_LOGF_FUNC=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_CYCLIC_MAX_CPU_TIME_US=50000 CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_CMD_SMBIOS=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_MEMINFO=y CONFIG_CMD_MEMINFO_MAP=y @@ -32,6 +34,8 @@ CONFIG_CMD_LSBLK=y CONFIG_CMD_CAT=y CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y +CONFIG_CMD_KASLRSEED=y +CONFIG_CMD_HASH=y CONFIG_CMD_EXT4_WRITE=y CONFIG_MAC_PARTITION=y CONFIG_OF_LIVE=y @@ -47,4 +51,5 @@ CONFIG_SYSRESET=y CONFIG_VIDEO=y CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_CONSOLE_SCROLL_LINES=5 +CONFIG_FAT_WRITE=y CONFIG_CMD_DHRYSTONE=y -- 2.43.0
participants (1)
-
Simon Glass