bmc_monitor_app: Support `FirmwarePlusLoader` for Nuvoton SoC
Support `FirmwarePlusLoader` duration for NCPM7XX or newer Nuvoton SoC.
This will mitigate the inaccuracy caused by SoC not supporting Firmware
time and Loader time.
Tested:
\# Check FirmwarePlusLoader time is correct on the BMC with Nuvoton SoC
```
bmc:~# cat /usr/share/boot_time_monitor/bmc_durations.csv.completed
Firmware,0
Loader,0
Kernel,38706
InitRD,0
Userspace,381086
FirmwarePlusLoader,43480
```
Google-Bug-Id: 296530445
Change-Id: Ie340bd5f9d4637f0be1f2915887200b3c3d03e12
Signed-off-by: Michael Shen <gpgpgp@google.com>
diff --git a/include/utils.hpp b/include/utils.hpp
index 60a11aa..1b061e4 100644
--- a/include/utils.hpp
+++ b/include/utils.hpp
@@ -88,6 +88,15 @@
*/
virtual std::string getDurPath(std::string_view nodeName,
bool wantCompleted) = 0;
+
+ /**
+ * Read 4 bytes data from target address
+ *
+ * @param[in] target - Target address
+ * @return 4 bytes data from the target address or std::nullopt if any error
+ * happens during `readMem4Bytes`
+ */
+ virtual std::optional<uint32_t> readMem4Bytes(uint32_t target) = 0;
};
/**
@@ -103,6 +112,7 @@
bool wantCompleted) override;
std::string getDurPath(std::string_view nodeName,
bool wantCompleted) override;
+ std::optional<uint32_t> readMem4Bytes(uint32_t target) override;
};
/**
diff --git a/meson.build b/meson.build
index cd5e04f..e6db5bc 100644
--- a/meson.build
+++ b/meson.build
@@ -17,6 +17,10 @@
sdbusplus_dep = dependency('sdbusplus', required : false)
+if get_option('npcm7xx-or-newer').enabled()
+ add_project_arguments('-DNPCM7XX_OR_NEWER', language:'cpp')
+endif
+
generated_sources = []
generated_others = []
if get_option('yocto').disabled()
diff --git a/meson_options.txt b/meson_options.txt
index ad4edc1..c173694 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,2 +1,3 @@
-option('tests', type: 'feature', description: 'Build tests')
-option('yocto', type : 'feature', value : 'disabled')
+option('tests', type: 'feature', description: 'Build tests', value : 'enabled')
+option('yocto', type : 'feature', description : 'Using yocto build or not', value : 'disabled')
+option('npcm7xx-or-newer', type : 'feature', description: 'The BMC is using Nuvoton NCPM7XX+ or not', value : 'disabled')
diff --git a/src/bmc_monitor_app.cpp b/src/bmc_monitor_app.cpp
index 96e0bb0..1c42b6f 100644
--- a/src/bmc_monitor_app.cpp
+++ b/src/bmc_monitor_app.cpp
@@ -130,6 +130,21 @@
? (times.finish - times.userspace) /
kMillisecondPerSecond
: 0);
+
+#ifdef NPCM7XX_OR_NEWER
+ // NPCM7XX or newer Nuvoton BMC has a register that starts counting from SoC
+ // power on. Also uptime starts counting when kernel is up. Thus we can get
+ // (Firmware + Loader) time by `value[SEC_CNT_ADDR] - uptime`.
+ constexpr uint32_t SEC_CNT_ADDR = 0xF0801068;
+
+ auto powerOnSec = util->readMem4Bytes(SEC_CNT_ADDR);
+ auto upTimeMS = util->getUpTimeInMS();
+ if (powerOnSec != std::nullopt && upTimeMS != std::nullopt)
+ {
+ bootManager->setDuration("FirmwarePlusLoader",
+ powerOnSec.value() * 1000 - upTimeMS.value());
+ }
+#endif
}
} // namespace boot_time_monitor
diff --git a/src/utils.cpp b/src/utils.cpp
index 15eae76..42783d8 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -2,6 +2,9 @@
#include <fmt/printf.h>
+#include <boost/interprocess/file_mapping.hpp>
+#include <boost/interprocess/mapped_region.hpp>
+
#include <array>
#include <chrono>
#include <filesystem>
@@ -60,6 +63,44 @@
(wantCompleted ? kCompletedSuffix : "");
}
+std::optional<uint32_t> Util::readMem4Bytes(uint32_t target)
+{
+ // Typically the pageSize will be 4K/8K for 32 bit operating systems
+ uint32_t pageSize = boost::interprocess::mapped_region::get_page_size();
+ const uint32_t kPageMask = pageSize - 1;
+
+ uint32_t pageOffset = target & (~kPageMask);
+ uint32_t offsetInPage = target & kPageMask;
+
+ // Map `/dev/mem` to a region.
+ std::unique_ptr<boost::interprocess::file_mapping> fileMapping;
+ std::unique_ptr<boost::interprocess::mapped_region> MappedRegion;
+ try
+ {
+ fileMapping = std::make_unique<boost::interprocess::file_mapping>(
+ "/dev/mem", boost::interprocess::read_only);
+ // No need to unmap in the end.
+ MappedRegion = std::make_unique<boost::interprocess::mapped_region>(
+ *fileMapping, boost::interprocess::read_only, pageOffset,
+ pageSize * 2);
+
+ // MappedRegion->get_address() returns (void*) which needs to covert
+ // into (char*) to make `+ offsetInPage` work.
+ // Then converts it again into (uint32_t*) since we read 4 bytes.
+ return *(reinterpret_cast<uint32_t*>(
+ static_cast<char*>(MappedRegion->get_address()) + offsetInPage));
+ }
+ catch (const std::exception& e)
+ {
+ fmt::print(stderr, "[{}]: Throw exception: %s\n", __FUNCTION__,
+ e.what());
+ return std::nullopt;
+ }
+
+ fmt::print(stderr, "[{}]: Shouldn't go to this line.\n", __FUNCTION__);
+ return std::nullopt;
+}
+
FileUtil::FileUtil(std::string_view filename) : filename(filename)
{
fs::path dir = fs::path(filename).remove_filename();
diff --git a/test/include/mockups.hpp b/test/include/mockups.hpp
index fedae10..387e006 100644
--- a/test/include/mockups.hpp
+++ b/test/include/mockups.hpp
@@ -21,6 +21,7 @@
MOCK_METHOD(bool, isValidName, (std::string_view), (override));
MOCK_METHOD(std::string, getCPPath, (std::string_view, bool), (override));
MOCK_METHOD(std::string, getDurPath, (std::string_view, bool), (override));
+ MOCK_METHOD(std::optional<uint32_t>, readMem4Bytes, (uint32_t), (override));
};
class MockFileUtil : public FileUtilIface