diff --git a/tools/mccibootloader_image/README.md b/tools/mccibootloader_image/README.md index ed42766..16ff102 100644 --- a/tools/mccibootloader_image/README.md +++ b/tools/mccibootloader_image/README.md @@ -70,6 +70,8 @@ The following options are defined. Note that options can be mixed with the input
Update the input file in place.
-k file, --keyfile file
Read the signing key from file, which must be an OpenSSH ed25519 private key file, not password protected. The (insecure) keyfile test/mcci-test.pem is conventionally used for test purposes.
+
-V major[.minor[.patch]][-pre], --app-version major[.minor[.patch]][-pre]
+
Set the application version according to the argument.
-s, --sign
Compute the hash (as with -h, and then sign. A key file must be provided.
-v, --verbose
@@ -93,6 +95,7 @@ Program settings: --patch: false --keyfile: C:/mcci/projects/lora/bootloader/tools/mccibootloader_image/test/mcci-test.pem --comment: <> + --app-version: <> input: build/arm-none-eabi/release/McciBootloader_46xx output: build/arm-none-eabi/release/McciBootloader_46xx.elf diff --git a/tools/mccibootloader_image/i/mccibootloader_image.h b/tools/mccibootloader_image/i/mccibootloader_image.h index bbcfd77..4b41639 100644 --- a/tools/mccibootloader_image/i/mccibootloader_image.h +++ b/tools/mccibootloader_image/i/mccibootloader_image.h @@ -65,6 +65,49 @@ static_assert(offsetof(GuidWire_t, clock_seq_low) == 9, "GUID layout mismatch"); static_assert(offsetof(GuidWire_t, node) == 10, "GUID layout mismatch"); static_assert(sizeof(GuidWire_t) == GuidWire_t_SIZE, "GUID layout mismatch"); +/* versions */ +namespace McciVersion { + typedef std::uint32_t Version_t; + + // create a version number for comparison + constexpr std::uint32_t + makeVersion( + std::uint8_t major, std::uint8_t minor, std::uint8_t patch, std::uint8_t local = 0 + ) + { + return ((std::uint32_t)major << 24u) | ((std::uint32_t)minor << 16u) | ((std::uint32_t)patch << 8u) | (std::uint32_t)local; + } + + // extract major number from version + constexpr std::uint8_t + getMajor(std::uint32_t v) + { + return std::uint8_t(v >> 24u); + } + + // extract minor number from version + constexpr std::uint8_t + getMinor(std::uint32_t v) + { + return std::uint8_t(v >> 16u); + } + + // extract patch number from version + constexpr std::uint8_t + getPatch(std::uint32_t v) + { + return std::uint8_t(v >> 8u); + } + + // extract local number from version + constexpr std::uint8_t + getLocal(std::uint32_t v) + { + return std::uint8_t(v); + } +} // namespace McciVersion + + // a forward reference struct McciBootloader_AppInfo_Wire_t; @@ -85,6 +128,8 @@ struct App_t std::string progname; std::string keyfilename; std::vector fileimage; + McciVersion::Version_t appVersion; + bool fAppVersion; struct AppElf_t { @@ -118,6 +163,7 @@ struct App_t void readImage(); void writeImage(); void elfImagePrep(); + void setAppVersion(const string &versionString); Keyfile_ed25519_t keyfile; }; @@ -134,47 +180,6 @@ static constexpr const char *filebasename(const char *s) return pName; } -namespace McciVersion { - typedef std::uint32_t Version_t; - - // create a version number for comparison - constexpr std::uint32_t - makeVersion( - std::uint8_t major, std::uint8_t minor, std::uint8_t patch, std::uint8_t local = 0 - ) - { - return ((std::uint32_t)major << 24u) | ((std::uint32_t)minor << 16u) | ((std::uint32_t)patch << 8u) | (std::uint32_t)local; - } - - // extract major number from version - constexpr std::uint8_t - getMajor(std::uint32_t v) - { - return std::uint8_t(v >> 24u); - } - - // extract minor number from version - constexpr std::uint8_t - getMinor(std::uint32_t v) - { - return std::uint8_t(v >> 16u); - } - - // extract patch number from version - constexpr std::uint8_t - getPatch(std::uint32_t v) - { - return std::uint8_t(v >> 8u); - } - - // extract local number from version - constexpr std::uint8_t - getLocal(std::uint32_t v) - { - return std::uint8_t(v); - } -} // namespace McciVersion - // the layout of the image class uint32_le_t { diff --git a/tools/mccibootloader_image/i/mccibootloader_image_version.h b/tools/mccibootloader_image/i/mccibootloader_image_version.h index f3a9bcf..0ee9346 100644 --- a/tools/mccibootloader_image/i/mccibootloader_image_version.h +++ b/tools/mccibootloader_image/i/mccibootloader_image_version.h @@ -27,7 +27,7 @@ Copyright and License: #include "mccibootloader_image.h" constexpr McciVersion::Version_t kVersion = - McciVersion::makeVersion(0, 3, 1, 0); + McciVersion::makeVersion(0, 4, 0, 0); constexpr char kCopyright[] = "Copyright (C) 2021, MCCI Corporation"; #endif /* _mccibootloader_image_version_h_ */ diff --git a/tools/mccibootloader_image/src/image.cpp b/tools/mccibootloader_image/src/image.cpp index 35ff932..686e438 100644 --- a/tools/mccibootloader_image/src/image.cpp +++ b/tools/mccibootloader_image/src/image.cpp @@ -107,11 +107,30 @@ void App_t::readImage() { this->fatal("unexpected e_phoff value"); } - if (pElfIdent32->getPhnum() > 4) + + // display the program header entries + if (this->fVerbose) { - ostringstream msg; - msg << "ELF e_phnum > 4: " << pElfIdent32->getPhnum(); - this->fatal(msg.str()); + for (unsigned i = 0; i < pElfIdent32->getPhnum(); ++i) + { + const ElfIdent32_t::ProgramHeader_t ph { *pElfIdent32, i }; + + // dump the section + { + std::ostringstream msg; + + msg << "ELF: section " << i << ": " + << "vaddr(" << std::hex << ph.getVaddr() << ") " + << "paddr(" << std::hex << ph.getPaddr() << ") " + << "size(" << std::hex << ph.getMemsz() << ") " + << "vend(" << std::hex << ph.getVaddr() + ph.getMemsz() << ") " + << "offset(" << std::hex << ph.getOffset() << ") " + << "fsize(" << std::hex << ph.getFilesz() << ") " + << "flags(" << std::hex << ph.getFlags() << ")" + ; + this->verbose(msg.str()); + } + } } // empty the file image and set up the elf image @@ -129,23 +148,6 @@ void App_t::readImage() if (ph.getType() != decltype(ph.getType())::kLoad) this->fatal("section type is not loadable"); - // dump the section - if (this->fVerbose) - { - std::ostringstream msg; - - msg << "ELF: section " << i << ": " - << "vaddr(" << std::hex << ph.getVaddr() << ") " - << "paddr(" << std::hex << ph.getPaddr() << ") " - << "size(" << std::hex << ph.getMemsz() << ") " - << "vend(" << std::hex << ph.getVaddr() + ph.getMemsz() << ") " - << "offset(" << std::hex << ph.getOffset() << ") " - << "fsize(" << std::hex << ph.getFilesz() << ") " - << "flags(" << std::hex << ph.getFlags() << ")" - ; - this->verbose(msg.str()); - } - // we used to try skipping writable sections, but ... // that requires PHDRS in the link script. Instead, use // a heuristic to discard non-contiguous sections diff --git a/tools/mccibootloader_image/src/main.cpp b/tools/mccibootloader_image/src/main.cpp index ea14cb1..2ca6534 100644 --- a/tools/mccibootloader_image/src/main.cpp +++ b/tools/mccibootloader_image/src/main.cpp @@ -35,6 +35,10 @@ static void dumpAppInfo( McciBootloader_AppInfo_Wire_t const &appInfo ); +static std::string versionToString( + McciVersion::Version_t version + ); + /****************************************************************************\ | | Read-only data. @@ -196,6 +200,13 @@ void App_t::scanArgs(int argc, char **argv) this->usage("missing comment value"); this->pComment = *argv++; } + else if (arg == "-V" || arg == "--app-version") + { + if (*argv == nullptr) + this->usage("missing app-version value"); + + this->setAppVersion(string(*argv)); + } else if (arg == "--version") { std::cout << "mccibootloader_image v" @@ -257,6 +268,7 @@ void App_t::scanArgs(int argc, char **argv) << " --patch: " << this->fPatch << "\n" << " --keyfile: " << this->keyfilename << "\n" << " --comment: " << (pComment == NULL ? "<>": pComment) << "\n" + << " --app-version: " << (!this->fAppVersion ? "<>": versionToString(this->appVersion)) << "\n" << "\n" << "input: " << this->infilename << "\n" << "output: " @@ -271,17 +283,119 @@ void App_t::scanArgs(int argc, char **argv) void App_t::usage(const string &message) { string usage = message; - if (usage != "") + if (usage != "" && usage[usage.length() - 1] != '\n') { usage.append(": "); } usage.append("usage: "); usage.append(this->progname); - usage.append(" -[vsh k{keyfile} c{comment}] --[version sign hash comment {comment} dry-run add-time force-binary] infile [outfile]\n"); + usage.append(" -[vsh k{keyfile} c{comment} -V{app-version}] --[version sign hash app-version {version} comment {comment} dry-run add-time force-binary] infile [outfile]\n"); fprintf(stderr, "%s\n", usage.c_str()); exit(EXIT_FAILURE); } +static std::string versionToString(McciVersion::Version_t version) + { + ostringstream sVersion; + sVersion << unsigned(McciVersion::getMajor(version)) + << "." + << unsigned(McciVersion::getMinor(version)) + << "." + << unsigned(McciVersion::getPatch(version)) + ; + if (McciVersion::getLocal(version) != 0) + { + sVersion << "-" + << unsigned(McciVersion::getLocal(version)); + } + + return sVersion.str(); + } + +void App_t::setAppVersion( + const string &versionString + ) + { + unsigned i = 0; + + auto const failstring = [&versionString, &i](const string &message) -> string + { + string result; + result = message + ": "; + result += versionString.substr(0, i); + result += "["; + result += versionString.substr(i); + result += "]\n"; + return result; + }; + + auto const getn = [this, &versionString, &i, &failstring]() -> std::uint8_t + { + std::uint32_t result; + auto const n = versionString.length(); + bool err; + bool gotDigit; + + err = false; + gotDigit = false; + result = 0; + + while (i < n && !err) + { + auto c = versionString[i]; + if (! ('0' <= c && c <= '9')) + break; + + gotDigit = true; + result = result * 10 + (c - '0'); + if (result >= 256) + err = true; + else + ++i; + } + + if (! gotDigit) + err = true; + + if (err) + this->usage(failstring("illegal app-version syntax")); + + return result; + }; + auto const checkpunct = [&versionString, &i](char c) -> bool + { + auto const n = versionString.length(); + + if (i < n && versionString[i] == c) + { + ++i; + return true; + } + return false; + }; + + auto const major = getn(); + std::uint8_t minor, patch, prerelease; + minor = patch = prerelease = 0; + + if (checkpunct('.')) + { + minor = getn(); + if (checkpunct('.')) + patch = getn(); + } + if (checkpunct('-')) + { + prerelease = getn(); + } + + if (i < versionString.length()) + this->usage(failstring("illegal value for app-version")); + + this->appVersion = McciVersion::makeVersion(major, minor, patch, prerelease); + this->fAppVersion = true; + } + void dumpAppInfo( std::string const &s, McciBootloader_AppInfo_Wire_t const &appInfo @@ -296,21 +410,8 @@ void dumpAppInfo( << " posixTimestamp: " << std::setw(16) << std::setfill(' ') << appInfo.posixTimestamp.get() << "\n" << " comment: " << appInfo.comment.get() << "\n"; ; - auto version = appInfo.version.get(); - ostringstream sVersion; - sVersion << unsigned(McciVersion::getMajor(version)) - << "." - << unsigned(McciVersion::getMinor(version)) - << "." - << unsigned(McciVersion::getPatch(version)) - ; - if (McciVersion::getLocal(version) != 0) - { - sVersion << "-" - << unsigned(McciVersion::getLocal(version)); - } - std::cout << " version: " << std::setw(16) << std::setfill(' ') << sVersion.str(); + std::cout << " version: " << std::setw(16) << std::setfill(' ') << versionToString(appInfo.version.get()); std::cout << "\n"; std::cout << "\n"; } @@ -384,6 +485,12 @@ void App_t::addHeader() this->authSize ); + // set the version if one was provided + if (this->fAppVersion) + appInfo.version.put( + this->appVersion + ); + // add posix time if (this->fAddTime) {