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)
{