From 694af3dd23bdd235e1a916cd4ad410dd6ab0121d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 1 Nov 2021 22:36:05 -0500 Subject: [PATCH] main: redesign boot_out.txt writing New design: * capture output to a vstr * compare the complete vstr to boot_out.txt * rewrite if not a complete match This is resilient against future changes to the automatic text written to boot_out.txt. This also fixes rewriting boot_out.txt in the case where boot.py prints something. Perhaps it also saves a bit of code space. Some tricks: * no need to close a file in read mode * no need to switch on/off USB write access, going down to the oofatfs layer doesn't check it anyway --- main.c | 120 +++++++++++++------------------- py/makeversionhdr.py | 2 +- supervisor/serial.h | 4 +- supervisor/shared/micropython.c | 5 +- 4 files changed, 55 insertions(+), 76 deletions(-) diff --git a/main.c b/main.c index 9f3bead9c2..32ece4bbea 100755 --- a/main.c +++ b/main.c @@ -635,7 +635,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode) { return skip_repl; } -FIL* boot_output_file; +vstr_t *boot_output; STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { // If not in safe mode, run boot before initing USB and capture output in a file. @@ -645,67 +645,12 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { && safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL; - static const char * const boot_py_filenames[] = STRING_LIST("boot.py", "boot.txt"); - bool skip_boot_output = false; - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - FIL file_pointer; - #endif - - if (ok_to_run) { - - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - boot_output_file = &file_pointer; - - // Get the base filesystem. - FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs; - - bool have_boot_py = first_existing_file_in_list(boot_py_filenames) != NULL; - - // If there's no boot.py file that might write some changing output, - // read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents - // match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE. - // This saves wear and tear on the flash and also prevents filesystem damage if power is lost - // during the write, which may happen due to bobbling the power connector or weak power. - - static const size_t NUM_CHARS_TO_COMPARE = 160; - if (!have_boot_py && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { - - char file_contents[NUM_CHARS_TO_COMPARE]; - UINT chars_read = 0; - f_read(boot_output_file, file_contents, NUM_CHARS_TO_COMPARE, &chars_read); - f_close(boot_output_file); - skip_boot_output = - // + 2 accounts for \r\n. - chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 && - strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0; - } - - if (!skip_boot_output) { - // Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, - // in case power is momentary or will fail shortly due to, say a low, battery. - if (common_hal_mcu_processor_get_reset_reason() == RESET_REASON_POWER_ON) { - mp_hal_delay_ms(1500); - } - // USB isn't up, so we can write the file. - filesystem_set_internal_writable_by_usb(false); - f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); - - // Switch the filesystem back to non-writable by Python now instead of later, - // since boot.py might change it back to writable. - filesystem_set_internal_writable_by_usb(true); - - // Write version info to boot_out.txt. - mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); - // Write the board ID (board directory and ID on circuitpython.org) - mp_hal_stdout_tx_str("\r\n" "Board ID:"); - mp_hal_stdout_tx_str(CIRCUITPY_BOARD_ID); - mp_hal_stdout_tx_str("\r\n"); - } - #endif - - filesystem_flush(); + if (!ok_to_run) { + return; } + static const char * const boot_py_filenames[] = STRING_LIST("boot.py", "boot.txt"); + // Do USB setup even if boot.py is not run. supervisor_allocation* heap = allocate_remaining_memory(); @@ -716,20 +661,55 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { usb_set_defaults(); #endif - pyexec_result_t result = {0, MP_OBJ_NULL, 0}; - if (ok_to_run) { - bool found_boot = maybe_run_list(boot_py_filenames, &result); - (void) found_boot; + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + vstr_t boot_text; + vstr_init(&boot_text, 512); + boot_output = &boot_text; + #endif - #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - if (!skip_boot_output) { - f_close(boot_output_file); - filesystem_flush(); + // Write version info + mp_printf(&mp_plat_print, "%s\nBoard ID:%s\n", MICROPY_FULL_VERSION_INFO, CIRCUITPY_BOARD_ID); + + pyexec_result_t result = {0, MP_OBJ_NULL, 0}; + + bool found_boot = maybe_run_list(boot_py_filenames, &result); + (void) found_boot; + + + #ifdef CIRCUITPY_BOOT_OUTPUT_FILE + // Get the base filesystem. + fs_user_mount_t *vfs = (fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj; + FATFS *fs = &vfs->fatfs; + + boot_output = NULL; + bool write_boot_output = (common_hal_mcu_processor_get_reset_reason() == RESET_REASON_POWER_ON); + FIL boot_output_file; + if (f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { + char *file_contents = m_new(char, boot_text.alloc); + UINT chars_read; + if (f_read(&boot_output_file, file_contents, 1+boot_text.len, &chars_read) == FR_OK) { + write_boot_output = + (chars_read != boot_text.len) || (memcmp(boot_text.buf, file_contents, chars_read) != 0); } - boot_output_file = NULL; - #endif + // no need to f_close the file } + if (write_boot_output) { + // Wait 1 second before opening CIRCUITPY_BOOT_OUTPUT_FILE for write, + // in case power is momentary or will fail shortly due to, say a low, battery. + mp_hal_delay_ms(1000); + + // USB isn't up, so we can write the file. + // operating at the oofatfs (f_open) layer means the usb concurrent write permission + // is not even checked! + f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); + UINT chars_written; + f_write(&boot_output_file, boot_text.buf, boot_text.len, &chars_written); + f_close(&boot_output_file); + filesystem_flush(); + } + #endif + #if CIRCUITPY_USB // Some data needs to be carried over from the USB settings in boot.py // to the next VM, while the heap is still available. diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 9eca9525b2..12eaa3d3f1 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -107,7 +107,7 @@ def make_version_header(filename): #define MICROPY_VERSION_MINOR (%s) #define MICROPY_VERSION_MICRO (%s) #define MICROPY_VERSION_STRING "%s" -#define MICROPY_FULL_VERSION_INFO ("Adafruit CircuitPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME) +#define MICROPY_FULL_VERSION_INFO "Adafruit CircuitPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME """ % ( git_tag, git_hash, diff --git a/supervisor/serial.h b/supervisor/serial.h index 391a874e48..a9f45a8e38 100644 --- a/supervisor/serial.h +++ b/supervisor/serial.h @@ -34,9 +34,9 @@ #include "py/mpconfig.h" #ifdef CIRCUITPY_BOOT_OUTPUT_FILE -#include "lib/oofatfs/ff.h" +#include "py/misc.h" -extern FIL *boot_output_file; +extern vstr_t *boot_output; #endif void serial_early_init(void); diff --git a/supervisor/shared/micropython.c b/supervisor/shared/micropython.c index 76ac2bf5b7..24bc57ec12 100644 --- a/supervisor/shared/micropython.c +++ b/supervisor/shared/micropython.c @@ -59,9 +59,8 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { toggle_tx_led(); #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - if (boot_output_file != NULL) { - UINT bytes_written = 0; - f_write(boot_output_file, str, len, &bytes_written); + if (boot_output != NULL) { + vstr_add_strn(boot_output, str, len); } #endif