esp32/ota: Implement ESP-IDF OTA functionality.

Implemented new functions:
* mark_app_invalid_rollback_and_reboot()
* check_rollback_is_possible()
* app_description()
* app_state()
* ota_begin()
* ota_write()
* ota_write_with_offset() for ESP-IDF version >= 4.2
* ota_end()
* ota_abort() for ESP-IDF version >= 4.3

* create tests
* update documentation

esp32/ota: Implement ESP-IDF OTA functionality.
pull/7048/head
Emil Kondayan 2022-03-14 10:11:11 +02:00
rodzic 5114f2c1ea
commit 624174aa18
3 zmienionych plików z 423 dodań i 34 usunięć

Wyświetl plik

@ -133,15 +133,154 @@ methods to enable over-the-air (OTA) updates.
.. classmethod:: Partition.mark_app_valid_cancel_rollback()
Signals that the current boot is considered successful.
Calling ``mark_app_valid_cancel_rollback`` is required on the first boot of a new
partition to avoid an automatic rollback at the next boot.
Signals that the current boot is considered successful by writing to the "otadata"
partition. Calling ``mark_app_valid_cancel_rollback`` is required on the first boot of a
new partition to avoid an automatic rollback at the next boot.
This uses the ESP-IDF "app rollback" feature with "CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE"
and an ``OSError(-261)`` is raised if called on firmware that doesn't have the
feature enabled.
It is OK to call ``mark_app_valid_cancel_rollback`` on every boot and it is not
necessary when booting firmware that was loaded using esptool.
.. classmethod:: Partition.mark_app_invalid_rollback_and_reboot()
Mark the current app partition invalid by writing to the "otadata"
partition, rollback to the previous workable app and then reboots.
If the rollback is sucessfull, the device will reset. If the flash does not have
at least one valid app (except the running app) then rollback is not possible.
If the "CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE" option is set, and a reset occurs without
calling either
``mark_app_valid_cancel_rollback()`` or ``mark_app_invalid_rollback_and_reboot()``
function then the application is rolled back.
This uses the ESP-IDF "app rollback" feature with "CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE"
and an ``OSError(-261)`` is raised if called on firmware that doesn't have the
feature enabled.
.. classmethod:: Partition.check_rollback_is_possible()
Returns True if at least one valid app is found(except the running one)
Returns False otherwise.
Checks if there is a bootable application on the slots which can be booted in case of
rollback. For an application to be considered bootable, the following conditions
must be met: the app must be marked to be valid(marked in otadata as not UNDEFINED,
INVALID or ABORTED and crc is good); must be marked bootable; secure_version of
app >= secure_version of efuse (if anti-rollback is enabled).
.. method:: Partition.app_description()
Returns a 7-tuple ``(secure_version, version, project_name, compile_time, compile_date,
idf_version, elf_sha256)`` which is a description of the app partition pointed by the
object.
If the object does not contain an app partition, OsError exception will be raised:
``ESP_ERR_NOT_FOUND`` no app description structure is found. Magic word is incorrect.
``ESP_ERR_NOT_SUPPORTED`` Partition is not application.
``ESP_ERR_INVALID_ARG`` Partitions offset exceeds partition size.
``ESP_ERR_INVALID_SIZE`` Read would go out of bounds of the partition.
.. method:: Partition.app_state()
Returns the app state of a valid ota partition. It can be one of the following strings:
``new``: Monitor the first boot. In bootloader this state is changed to "pending verify"
``verify``: First boot for this app. If this state persists during second boot, then it
will be changed to ``aborted``
``valid``: App was confirmed as workable. App can boot and work without limits
``invalid``: App was confirmed as non-workable. This app will not be selected to
boot at all
``aborted``: App could not confirmed as workable or non-workable. In bootloader
"pending verify" state will be changed to ``aborted``. This app will not be selected
to boot at all
``undefined``: App can boot and work without limits
One of the following OsError can be raised:
``ESP_ERR_NOT_SUPPORTED``: Partition is not ota.
``ESP_ERR_NOT_FOUND``: Partition table does not have otadata or state was not found for
given partition.
.. method:: Partition.ota_begin(image_size)
Prepares the partition for an OTA update and start the process of updating.
The target partition is erased to the specified image size. If the size of the
artition is not known in advance, the entire partition is eraesd.
Note: This function is available since ESP-IDF version 4.3
Note: If the rollback option is enabled and the running application has the
"pending verify" state then it will lead to the ESP_ERR_OTA_ROLLBACK_INVALID_STATE error.
Confirm the running app before to run download a new app, use
mark_app_valid_cancel_rollback() function
``image_size``: The size of the image to be written. 0 indicates a partition of unknown
size. If you know the size of the partition in advance, you can pass the size in bytes.
The default value is "0"
Returns an integer handle, associated with the ota update process. The update
process must be ended by calling ``ota_end()`. Since ESP-IDF version 4.3,
an update process can also be ended by ``ota_abort()``.
An OsError can be raised if there is an error with the update process:
``ESP_ERR_INVALID_ARG``: Partition doesnt point to an OTA app partition
``ESP_ERR_NO_MEM``: Cannot allocate memory for OTA operation
``ESP_ERR_OTA_PARTITION_CONFLICT``: Partition holds the currently running firmware,
cannot update in place
``ESP_ERR_NOT_FOUND``: Partition argument not found in partition table
``ESP_ERR_OTA_SELECT_INFO_INVALID``: The OTA data partition contains invalid data
``ESP_ERR_INVALID_SIZE``: Partition doesnt fit in configured flash size
``ESP_ERR_FLASH_OP_TIMEOUT`` or ``ESP_ERR_FLASH_OP_FAIL``: Flash write failed
``ESP_ERR_OTA_ROLLBACK_INVALID_STATE``: If the running app has not confirmed state. Before
performing an update, the application must be valid
.. method:: Partition.ota_write(handle, buf)
Write OTA update data to the target partition. This function can be called multiple times
as data is received during the OTA operation. Data is written sequentially to the partition.
``handle``: The handle returned by ``ota_begin()``
``buf``: Data buffer to write
An OsError can be raised if there is an error with the update process:
``ESP_ERR_INVALID_ARG``: Handle is invalid
``ESP_ERR_OTA_VALIDATE_FAILED``: First byte of image contains invalid app image magic byte
``ESP_ERR_FLASH_OP_TIMEOUT`` or ``ESP_ERR_FLASH_OP_FAIL``: Flash write failed
``ESP_ERR_OTA_SELECT_INFO_INVALID``: OTA data partition has invalid contents
.. method:: Partition.ota_write_with_offset(handle, buffer, offset)
Write OTA update data to the target partition. This function writes data in non contiguous
manner. If flash encryption is enabled, data should be 16 byte aligned.
Note: This function is available since ESP-IDF version 4.2
Note: While performing OTA, if the packets arrive out of order, esp_ota_write_with_offset()
can be used to write data in non contiguous manner. Use of esp_ota_write_with_offset() in
combination with esp_ota_write() is not recommended.
An OsError can be raised if there is an error with the update process:
``ESP_ERR_INVALID_ARG``: handle is invalid
``ESP_ERR_OTA_VALIDATE_FAILED``: First byte of image contains invalid app image magic byte
``ESP_ERR_FLASH_OP_TIMEOUT`` or ``ESP_ERR_FLASH_OP_FAIL``: Flash write failed
``ESP_ERR_OTA_SELECT_INFO_INVALID``: OTA data partition has invalid contents
.. method:: Partition.ota_end(handle)
Finish the OTA update process and validate newly written app image.
An OsError can be raised if there is an error with the update process:
``ESP_ERR_NOT_FOUND``: OTA handle was not found
``ESP_ERR_INVALID_ARG``: Handle was never written to
``ESP_ERR_OTA_VALIDATE_FAILED``: OTA image is invalid (either not a valid app image, or
if secure boot is enabled - signature failed to verify)
``ESP_ERR_INVALID_STATE``: If flash encryption is enabled, this result indicates an internal
error writing the final encrypted bytes to flash
.. method:: Partition.ota_abort(handle)
Aborts the OTA process and frees resources
An OsError can be raised if there is an error:
``ESP_ERR_NOT_FOUND``: OTA handle was not found
Constants
~~~~~~~~~

Wyświetl plik

@ -265,6 +265,130 @@ static MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_mark_app_valid_cancel_rollback_
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(esp32_partition_mark_app_valid_cancel_rollback_obj,
MP_ROM_PTR(&esp32_partition_mark_app_valid_cancel_rollback_fun_obj));
static mp_obj_t esp32_partition_mark_app_invalid_rollback_and_reboot(mp_obj_t cls_in) {
check_esp_err(esp_ota_mark_app_invalid_rollback_and_reboot());
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_mark_app_invalid_rollback_and_reboot_fun_obj,
esp32_partition_mark_app_invalid_rollback_and_reboot);
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(esp32_mark_app_invalid_rollback_and_reboot_obj,
MP_ROM_PTR(&esp32_partition_mark_app_invalid_rollback_and_reboot_fun_obj));
static mp_obj_t esp32_check_rollback_is_possible(mp_obj_t cls_in) {
return mp_obj_new_bool(esp_ota_check_rollback_is_possible());
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_check_rollback_is_possible_fun_obj, esp32_check_rollback_is_possible);
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(esp32_check_rollback_is_possible_obj, MP_ROM_PTR(&esp32_check_rollback_is_possible_fun_obj));
static mp_obj_t esp32_app_description(mp_obj_t self_in) {
esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in);
esp_app_desc_t app;
check_esp_err(esp_ota_get_partition_description(self->part, &app));
mp_obj_t tuple[] = {
mp_obj_new_int_from_uint(app.secure_version),
mp_obj_new_str(app.version, strlen(app.version)),
mp_obj_new_str(app.project_name, strlen(app.project_name)),
mp_obj_new_str(app.time, strlen(app.time)),
mp_obj_new_str(app.date, strlen(app.date)),
mp_obj_new_str(app.idf_ver, strlen(app.idf_ver)),
mp_obj_new_bytes(app.app_elf_sha256, 32)
};
return mp_obj_new_tuple(7, tuple);
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_app_description_obj, esp32_app_description);
static mp_obj_t esp32_app_get_state(mp_obj_t self_in) {
esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in);
char *ret = NULL;
esp_ota_img_states_t state;
check_esp_err(esp_ota_get_state_partition(self->part, &state));
switch (state) {
// Monitor the first boot. In bootloader this state is changed to ESP_OTA_IMG_PENDING_VERIFY.
case ESP_OTA_IMG_NEW:
ret = "new";
break;
// First boot for this app. If this state persists during second boot, then it will be changed to ABORTED.
case ESP_OTA_IMG_PENDING_VERIFY:
ret = "verify";
break;
// App was confirmed as workable. App can boot and work without limits.
case ESP_OTA_IMG_VALID:
ret = "valid";
break;
// App was confirmed as non-workable. This app will not be selected to boot at all.
case ESP_OTA_IMG_INVALID:
ret = "invalid";
break;
// App could not confirmed as workable or non-workable. In bootloader IMG_PENDING_VERIFY state will be changed to IMG_ABORTED. This app will not be selected to boot at all.
case ESP_OTA_IMG_ABORTED:
ret = "aborted";
break;
// App can boot and work without limits.
default:
ret = "undefined";
}
return mp_obj_new_str(ret, strlen(ret));
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_app_get_state_obj, esp32_app_get_state);
static mp_obj_t esp32_ota_begin(size_t n_args, const mp_obj_t *args) {
esp32_partition_obj_t *self = MP_OBJ_TO_PTR(args[0]);
esp_ota_handle_t handle;
size_t image_size = 0;
if (n_args == 2) {
image_size = mp_obj_get_int(args[1]);
}
check_esp_err(esp_ota_begin(self->part, image_size, &handle));
return mp_obj_new_int_from_uint(handle);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_ota_begin_obj, 1, 2, esp32_ota_begin);
static mp_obj_t esp32_ota_write(mp_obj_t self_in, const mp_obj_t handle_in, const mp_obj_t data_in) {
const esp_ota_handle_t handle = mp_obj_get_int(handle_in);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ);
check_esp_err(esp_ota_write(handle, bufinfo.buf, bufinfo.len));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_3(esp32_ota_write_obj, esp32_ota_write);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
static mp_obj_t esp32_ota_write_with_offset(size_t n_args, const mp_obj_t *args) {
esp_ota_handle_t handle = mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
const uint32_t offset = mp_obj_get_int(args[3]);
check_esp_err(esp_ota_write_with_offset(handle, bufinfo.buf, bufinfo.len, offset));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_ota_write_with_offset_obj, 4, 4, esp32_ota_write_with_offset);
#endif
static mp_obj_t esp32_ota_end(mp_obj_t self_in, const mp_obj_t handle_in) {
const esp_ota_handle_t handle = mp_obj_get_int(handle_in);
check_esp_err(esp_ota_end(handle));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ota_end_obj, esp32_ota_end);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
static mp_obj_t esp32_ota_abort(mp_obj_t self_in, const mp_obj_t handle_in) {
esp_ota_handle_t handle = mp_obj_get_int(handle_in);
check_esp_err(esp_ota_abort(handle));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_2(esp32_ota_abort_obj, esp32_ota_abort);
#endif
static const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&esp32_partition_find_obj) },
@ -275,8 +399,22 @@ static const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_boot), MP_ROM_PTR(&esp32_partition_set_boot_obj) },
{ MP_ROM_QSTR(MP_QSTR_mark_app_valid_cancel_rollback), MP_ROM_PTR(&esp32_partition_mark_app_valid_cancel_rollback_obj) },
{ MP_ROM_QSTR(MP_QSTR_mark_app_invalid_rollback_and_reboot), MP_ROM_PTR(&esp32_mark_app_invalid_rollback_and_reboot_obj) },
{ MP_ROM_QSTR(MP_QSTR_check_rollback_is_possible), MP_ROM_PTR(&esp32_check_rollback_is_possible_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_next_update), MP_ROM_PTR(&esp32_partition_get_next_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_app_description), MP_ROM_PTR(&esp32_app_description_obj) },
{ MP_ROM_QSTR(MP_QSTR_app_state), MP_ROM_PTR(&esp32_app_get_state_obj) },
{ MP_ROM_QSTR(MP_QSTR_ota_begin), MP_ROM_PTR(&esp32_ota_begin_obj) },
{ MP_ROM_QSTR(MP_QSTR_ota_write), MP_ROM_PTR(&esp32_ota_write_obj) },
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
{ MP_ROM_QSTR(MP_QSTR_ota_write_with_offset), MP_ROM_PTR(&esp32_ota_write_with_offset_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_ota_end), MP_ROM_PTR(&esp32_ota_end_obj) },
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
{ MP_ROM_QSTR(MP_QSTR_ota_abort), MP_ROM_PTR(&esp32_ota_abort_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_INT(ESP32_PARTITION_BOOT) },
{ MP_ROM_QSTR(MP_QSTR_RUNNING), MP_ROM_INT(ESP32_PARTITION_RUNNING) },
{ MP_ROM_QSTR(MP_QSTR_TYPE_APP), MP_ROM_INT(ESP_PARTITION_TYPE_APP) },

Wyświetl plik

@ -1,6 +1,7 @@
# Test ESP32 OTA updates, including automatic roll-back.
# Running this test requires firmware with an OTA Partition, such as the GENERIC_OTA "board".
# This test also requires patience as it copies the boot partition into the other OTA slot.
# The test runs two times to test both copy functions: ota_write() and ota_write_with_offset()
import machine
from esp32 import Partition
@ -21,13 +22,20 @@ def log(*args):
print(*args)
# replace boot.py with the test code that will run on each reboot
import os
# If step does not exists, this means that this is the first pass
try:
os.rename("boot.py", "boot-orig.py")
from step import PASS
except:
pass
PASS = 1
# replace boot.py with the test code that will run on each reboot
import os
try:
os.rename("boot.py", "boot-orig.py")
except:
pass
with open("boot.py", "w") as f:
f.write("DEBUG=" + str(DEBUG))
f.write(
@ -40,57 +48,66 @@ cur_name = cur.info()[4]
def log(*args):
if DEBUG: print(*args)
from step import STEP, EXPECT
from step import STEP, EXPECT, PASS
log("Running partition: " + cur_name + " STEP=" + str(STEP) + " EXPECT=" + EXPECT)
if cur_name != EXPECT:
print("\\x04FAILED: step " + str(STEP) + " expected " + EXPECT + " got " + cur_name + "\\x04")
print("FAILED: step " + str(STEP) + " expected " + EXPECT + " got " + cur_name + "\\n")
if STEP == 0:
log("Not confirming boot ok and resetting back into first")
nxt = cur.get_next_update()
with open("step.py", "w") as f:
f.write("STEP=1\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\n")
f.write("STEP=1\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\nPASS=" + str(PASS) + "\\n")
machine.reset()
elif STEP == 1:
log("Booting into second partition again")
nxt = cur.get_next_update()
nxt.set_boot()
with open("step.py", "w") as f:
f.write("STEP=2\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\n")
f.write("STEP=2\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\nPASS=" + str(PASS) + "\\n")
machine.reset()
elif STEP == 2:
log("Confirming boot ok and rebooting into same partition")
Partition.mark_app_valid_cancel_rollback()
with open("step.py", "w") as f:
f.write("STEP=3\\nEXPECT=\\"" + cur_name + "\\"\\n")
f.write("STEP=3\\nEXPECT=\\"" + cur_name + "\\"\\nPASS=" + str(PASS) + "\\n")
machine.reset()
elif STEP == 3:
log("Booting into original partition")
log("Mark new app invalid and revert to the original partition!")
nxt = cur.get_next_update()
nxt.set_boot()
with open("step.py", "w") as f:
f.write("STEP=4\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\n")
machine.reset()
f.write("STEP=4\\nEXPECT=\\"" + nxt.info()[4] + "\\"\\nPASS=" + str(PASS) + "\\n")
if Partition.check_rollback_is_possible():
Partition.mark_app_invalid_rollback_and_reboot()
elif STEP == 4:
log("Confirming boot ok and DONE!")
Partition.mark_app_valid_cancel_rollback()
import os
os.remove("step.py")
os.remove("boot.py")
os.rename("boot-orig.py", "boot.py")
print("\\nSUCCESS!\\n\\x04\\x04")
if PASS == 1:
with open("step.py", "w") as f:
f.write("STEP=0\\nEXPECT=\\"" + cur_name + "\\"\\nPASS=2\\n")
with open("boot.py", "w") as f:
f.write("import partition_ota")
print("\\nGoing for pass 2\\n")
elif PASS == 2:
log("Confirming boot ok and DONE!")
Partition.mark_app_valid_cancel_rollback()
import os
os.remove("step.py")
os.remove("boot.py")
os.rename("boot-orig.py", "boot.py")
print("\\nSUCCESS!\\n")
machine.reset()
"""
)
def copy_partition(src, dest):
def copy_partition_incr(src, dest):
log("Partition copy: {} --> {}".format(src.info(), dest.info()))
sz = src.info()[3]
if dest.info()[3] != sz:
raise ValueError("Sizes don't match: {} vs {}".format(sz, dest.info()[3]))
addr = 0
blk = bytearray(4096)
addr = 0
handle = dest.ota_begin()
while addr < sz:
if sz - addr < 4096:
blk = blk[: sz - addr]
@ -98,20 +115,115 @@ def copy_partition(src, dest):
# need to show progress to run-tests.py else it times out
print(" ... 0x{:06x}".format(addr))
src.readblocks(addr >> 12, blk)
dest.writeblocks(addr >> 12, blk)
dest.ota_write(handle, blk)
addr += len(blk)
dest.ota_end(handle)
# get things started by copying the current partition into the next slot and rebooting
print("Copying current to next partition")
def copy_partition_offset(src, dest):
log("Partition copy: {} --> {}".format(src.info(), dest.info()))
sz = src.info()[3]
blk = bytearray(4096)
addr = 0
handle = dest.ota_begin()
while addr < sz:
if sz - addr < 4096:
blk = blk[: sz - addr]
if addr & 0xFFFF == 0:
# need to show progress to run-tests else it times out
print(" ... 0x{:06x}".format(addr))
src.readblocks(addr >> 12, blk)
dest.ota_write_with_offset(handle, blk, addr)
addr += len(blk)
dest.ota_end(handle)
# Test current partition if it holds a valid app description.
try:
if cur.app_state() == "valid":
print("TEST [SUCESSFUL]: Current partition contains a valid application")
else:
print(
'TEST [FAILED]: Current partition state is "{}" expected "valid"'.format(
cur.app_state()
)
)
raise SystemExit
except OSError as e:
print("TEST [FAILED]: Current partition state error is {}".format(e))
# Test to overwrite the running partition
try:
copy_partition_incr(cur, cur)
except OSError as e:
print("TEST [SUCESSFUL]: Prevent overwriting the running partition. Message: {}".format(e))
else:
print("TEST [FAILED]: Current partition overwritten")
raise SystemExit
nxt = cur.get_next_update()
copy_partition(cur, nxt)
# Test if the next partition already contains an app. This test is better to be conducted on an empty partition
try:
nxt.app_state()
except OSError as e:
print(
"TEST [SUCESSFUL]: The next ota partition does not contain an app. Message: {}".format(e)
)
else:
print(
'TEST [WARNING]: The state of the next ota partition is "{}". You should run the test on a freshly erased device'.format(
nxt.app_state()
)
)
# Get things started by copying the current partition into the next slot and rebooting
# On the first pass use the incremental copy method
# On the second pass use the offset copy method
print("Copying current to next partition")
if PASS == 1:
# Get ESP-IDF version as integer
cur_desc = cur.app_description()[5]
ver = int(cur_desc[1] + cur_desc[3])
if ver >= 42:
copy_partition_offset(cur, nxt)
else:
print(
"TEST [SKIP]: Function 'ota_write_with_offset' is available since ESP-IDF version 4.2. Current ESP-IDF version {}.{}".format(
cur_desc[1], cur_desc[3]
)
)
PASS = 2
copy_partition_incr(cur, nxt)
else:
copy_partition_incr(cur, nxt)
print("Partition copied, booting into it")
nxt.set_boot()
# Test next partition state
try:
nxt_sha = nxt.app_description()[6]
cur_sha = cur.app_description()[6]
if cur_sha == nxt_sha:
print("TEST [SUCESSFUL]: The SHA256 of current and next partition are the same")
else:
print(
"TEST [FAILED]: The SHA256 of current and next partition are different. Must be the same"
)
raise SystemExit
except OSError as e:
print("TEST [FAILED]: Next partition is not valid. Error: {}".format(e))
raise SystemExit
try:
nxt.set_boot()
except OSError as e:
print("TEST [FAILED]: Can not set the new partition for the next boot. Error: {}".format(e))
else:
print("TEST [SUCESSFUL]: The next partition is set for boot")
# the step.py file is used to keep track of state across reboots
# EXPECT is the name of the partition we expect to reboot into
with open("step.py", "w") as f:
f.write('STEP=0\nEXPECT="' + nxt.info()[4] + '"\n')
f.write('STEP=0\nEXPECT="' + nxt.info()[4] + '"\nPASS=' + str(PASS) + "\n")
machine.reset()