mirror of
https://github.com/apache/nuttx-apps.git
synced 2025-10-17 15:32:21 +08:00
boot: add NuttX bootloader with update and recovery support
This commit adds NuttX based bootloader with the support for image update and recovery if not confirmed. The algorithm utilizes three flash partitions: primary (image runs from this area), secondary and tertiary. Secondary and tertiary areas are used for update upload and recovery. The update is performed by simple copy from update area to primary area with recovery being created in recovery area if not already present. Once image is confirmed by the user, the image in update area is confirmed as well, update area becomes recovery area and vice versa. This means the recovery is always present (except for the first update) and subsequent updates just copy image from update to primary. This makes the update significantly faster and more considerable to flash wear while keeping the recovery/revert possibility. A header (aligned to flash's erase size) must be added to the beginning of the image. Python script nximage.py can be used to prepend this header to built binary. The algorithm also uses one erase page at the end of a partition (partition, not image!) to store flags used to indicate image confirm status and to detect update/recovery partitions. Any program uploading update image to the update partition has to erase this page for the boot to work correctly! The algorithm implementation is based on a patch initially developed for MCUboot project but rejected by the project's maintainers https://github.com/mcu-tools/mcuboot/pull/1902 Signed-off-by: Michal Lenc <michallenc@seznam.cz>
This commit is contained in:

committed by
Alan C. Assis

parent
242b947342
commit
61fec07c62
115
boot/nxboot/tools/nximage.py
Normal file
115
boot/nxboot/tools/nximage.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""Python script that prepares the NuttX image to be used with NX bootloader"""
|
||||
|
||||
import argparse
|
||||
import io
|
||||
import os
|
||||
import struct
|
||||
import zlib
|
||||
|
||||
import semantic_version
|
||||
|
||||
|
||||
class NxImage:
|
||||
def __init__(
|
||||
self, path: str, result: str, version: str, header_size: int, primary: bool
|
||||
) -> None:
|
||||
self.path = path
|
||||
self.result = result
|
||||
self.size = os.stat(path).st_size
|
||||
self.version = semantic_version.Version(version)
|
||||
self.header_size = header_size
|
||||
self.primary = primary
|
||||
self.crc = 0
|
||||
|
||||
with open(path, "rb") as f:
|
||||
while data := f.read(io.DEFAULT_BUFFER_SIZE):
|
||||
self.crc = zlib.crc32(data, self.crc)
|
||||
|
||||
def __repr__(self):
|
||||
repr = (
|
||||
"<NxImage\n"
|
||||
f" path: {self.path}\n"
|
||||
f" result: {self.result}\n"
|
||||
f" fsize: {self.size}\n"
|
||||
f" version: {self.version}\n"
|
||||
f" header_size: {self.header_size}\n"
|
||||
f" primary: {self.primary}\n"
|
||||
f" crc: {self.crc}\n"
|
||||
f">"
|
||||
)
|
||||
return repr
|
||||
|
||||
def add_header(self):
|
||||
with open(self.path, "r+b") as src, open(self.result, "w+b") as dest:
|
||||
if self.primary:
|
||||
dest.write(b"\xb1\xab\xa0\xac")
|
||||
else:
|
||||
dest.write(b"\x4e\x58\x4f\x53")
|
||||
dest.write(struct.pack("<I", self.size))
|
||||
if self.primary:
|
||||
dest.write(struct.pack("<I", 0xFFFFFFFF))
|
||||
else:
|
||||
dest.write(struct.pack("<I", self.crc))
|
||||
dest.write(struct.pack("<H", self.version.major))
|
||||
dest.write(struct.pack("<H", self.version.minor))
|
||||
dest.write(struct.pack("<H", self.version.patch))
|
||||
if not self.version.prerelease:
|
||||
dest.write(struct.pack("@110s", b"\x00"))
|
||||
else:
|
||||
dest.write(
|
||||
struct.pack("@110s", bytes(self.version.prerelease[0], "utf-8"))
|
||||
)
|
||||
dest.write(bytearray(b"\xff") * (self.header_size - 128))
|
||||
while data := src.read(io.DEFAULT_BUFFER_SIZE):
|
||||
dest.write(data)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
"""Parse passed arguments and return result."""
|
||||
parser = argparse.ArgumentParser(description="Tool for Nuttx Bootloader")
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
default="0.0.0",
|
||||
help="Image version according to Semantic Versioning 2.0.0.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--header_size",
|
||||
type=lambda x: int(x, 0),
|
||||
default=0x200,
|
||||
help="Size of the image header.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--primary",
|
||||
action="store_true",
|
||||
help="Primary image intended to be uploaded directly to primary memory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
action="store_true",
|
||||
help="Verbose output. This prints information abourt the created image.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"PATH",
|
||||
default="nuttx.bin",
|
||||
help="Path to the NuttX image.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"RESULT",
|
||||
default="nuttx.img",
|
||||
help="Path where the resulting NuttX image is stored.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
image = NxImage(
|
||||
args.PATH, args.RESULT, args.version, args.header_size, args.primary
|
||||
)
|
||||
image.add_header()
|
||||
if args.v:
|
||||
print(image)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user