support multiple device trees in the same BMC image
This commit checks for current system type and if it is changed from last boot,
it loads new/appropriate device tree and reboot the system.
Tested-
=========================================
Test Case 1: first boot when env not defined
triggerred system vpd collection-
DBG: systemType is: 50001000
DBG: newDeviceTree: fw_dt2
U-Boot environment is not set. Updating...
DBG: rebooting...
root@rainier:/tmp# Connection to rain4bmc closed by remote host.
Connection to rain4bmc closed.
========================================
Test case 2-
triggerred system vpd collection-
DBG: systemType is: 50001001
DBG: newDeviceTree: fw_dt1
U-Boot environment is not updated. Updating...
DBG: rebooting...
root@rainier:/tmp# Connection to rain4bmc closed by remote host.
Connection to rain4bmc closed.
alpana07> ssh -k root@rain4bmc
root@rain4bmc's password:
printenv
...
bootside=b
devTree=fw_dt1
root@rainier:~#
=========================================
Test case 3-
triggerred system vpd collection again-
DBG: systemType is: 50001001
DBG: newDeviceTree: fw_dt1
U-Boot environment is Updated.
root@rainier:~# fw_printenv
...
bootside=b
devTree=fw_dt1
=========================================
Change-Id: I28bce8ced4a970e1818b4f4f613bd062647a4d3a
Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 25b77f1..48b930c 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -11,6 +11,7 @@
#include <CLI/CLI.hpp>
#include <algorithm>
+#include <cstdarg>
#include <exception>
#include <filesystem>
#include <fstream>
@@ -30,6 +31,10 @@
using namespace openpower::vpd::memory::parser;
using namespace openpower::vpd::parser::interface;
+static const deviceTreeMap deviceTreeSystemTypeMap = {
+ {RAINIER_2U, "conf@aspeed-bmc-ibm-rainier-2u.dtb"},
+ {RAINIER_4U, "conf@aspeed-bmc-ibm-rainier-4u.dtb"}};
+
/**
* @brief Expands location codes
*/
@@ -300,6 +305,118 @@
return objects;
}
+/* It does nothing. Just an empty function to return null
+ * at the end of variadic template args
+ */
+string getCommand()
+{
+ return "";
+}
+
+/* This function to arrange all arguments to make command
+ */
+template <typename T, typename... Types>
+string getCommand(T arg1, Types... args)
+{
+ string cmd = " " + arg1 + getCommand(args...);
+
+ return cmd;
+}
+
+/* This API takes arguments and run that command
+ * returns output of that command
+ */
+template <typename T, typename... Types>
+static vector<string> executeCmd(T& path, Types... args)
+{
+ vector<string> stdOutput;
+ array<char, 128> buffer;
+
+ string cmd = path + getCommand(args...);
+
+ unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
+ if (!pipe)
+ {
+ throw runtime_error("popen() failed!");
+ }
+ while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
+ {
+ stdOutput.emplace_back(buffer.data());
+ }
+
+ return stdOutput;
+}
+
+/**
+ * @brief This API executes command to set environment variable
+ * And then reboot the system
+ * @param[in] key -env key to set new value
+ * @param[in] value -value to set.
+ */
+void setEnvAndReboot(const string& key, const string& value)
+{
+ // set env and reboot and break.
+ executeCmd("/sbin/fw_setenv", key, value);
+ // make dbus call to reboot
+ auto bus = sdbusplus::bus::new_default_system();
+ auto method = bus.new_method_call(
+ "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager", "Reboot");
+ bus.call_noreply(method);
+}
+
+/*
+ * @brief This API checks for env var fitconfig.
+ * If not initialised OR updated as per the current system type,
+ * update this env var and reboot the system.
+ *
+ * @param[in] systemType IM kwd in vpd tells about which system type it is.
+ * */
+void setDevTreeEnv(const string& systemType)
+{
+ string newDeviceTree;
+
+ if (deviceTreeSystemTypeMap.find(systemType) !=
+ deviceTreeSystemTypeMap.end())
+ {
+ newDeviceTree = deviceTreeSystemTypeMap.at(systemType);
+ }
+
+ string readVarValue;
+ bool envVarFound = false;
+
+ vector<string> output = executeCmd("/sbin/fw_printenv");
+ for (const auto& entry : output)
+ {
+ size_t pos = entry.find("=");
+ string key = entry.substr(0, pos);
+ if (key != "fitconfig")
+ {
+ continue;
+ }
+
+ envVarFound = true;
+ if (pos + 1 < entry.size())
+ {
+ readVarValue = entry.substr(pos + 1);
+ if (readVarValue.find(newDeviceTree) != string::npos)
+ {
+ // fitconfig is Updated. No action needed
+ break;
+ }
+ }
+ // set env and reboot and break.
+ setEnvAndReboot(key, newDeviceTree);
+ exit(0);
+ }
+
+ // check If env var Not found
+ if (!envVarFound)
+ {
+ setEnvAndReboot("fitconfig", newDeviceTree);
+ }
+}
+
/**
* @brief Populate Dbus.
* This method invokes all the populateInterface functions
@@ -312,7 +429,7 @@
*/
template <typename T>
static void populateDbus(const T& vpdMap, nlohmann::json& js,
- const string& filePath) //, const string &preIntrStr) {
+ const string& filePath)
{
inventory::InterfaceMap interfaces;
inventory::ObjectMap objects;
@@ -412,12 +529,12 @@
}
string imValStr = oss.str();
- if (imValStr == SYSTEM_4U) // 4U
+ if (imValStr == RAINIER_4U) // 4U
{
target = INVENTORY_JSON_4U;
}
- else if (imValStr == SYSTEM_2U) // 2U
+ else if (imValStr == RAINIER_2U) // 2U
{
target = INVENTORY_JSON_2U;
}
@@ -434,6 +551,9 @@
inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
objects.insert(primeObject.begin(), primeObject.end());
+
+ // set the U-boot environment variable for device-tree
+ setDevTreeEnv(imValStr);
}
// Notify PIM
@@ -468,9 +588,7 @@
}
Binary vpdVector = getVpdDataInVector(js, file);
-
- ParserInterface* parser =
- ParserFactory::getParser(std::move(vpdVector));
+ ParserInterface* parser = ParserFactory::getParser(move(vpdVector));
variant<KeywordVpdMap, Store> parseResult;
parseResult = parser->parse();