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
This commit is contained in:
Jeff Epler 2021-11-01 22:36:05 -05:00
parent 0a02974505
commit 694af3dd23
4 changed files with 55 additions and 76 deletions

120
main.c
View File

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

View File

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

View File

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

View File

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