Updated Get Firmware Root Certficate data command
Updated IPMI Get Firmware Root Certficate data command(0x25) for PFR
This command works only for PFR enabled platforms.
Tested:
- Enabled INTEL_PFR_ENABLED flag and verified the below command
ipmitool -I dbus raw 0x08 0x25
Returns proper data.
- Disabled INTEL_PFR_ENABLED flag and verified
Returns error.
Signed-off-by: Rajashekar Gade Reddy <raja.sekhar.reddy.gade@linux.intel.com>
Change-Id: I8a6d0c939d26a8e8058dc35b26c6f78228ea8e5c
diff --git a/include/spiDev.hpp b/include/spiDev.hpp
new file mode 100644
index 0000000..0ebe6e5
--- /dev/null
+++ b/include/spiDev.hpp
@@ -0,0 +1,70 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#pragma once
+#include <experimental/filesystem>
+
+class SPIDev
+{
+ private:
+ int fd = -1;
+
+ public:
+ SPIDev() = delete;
+ SPIDev(const SPIDev&) = delete;
+ SPIDev& operator=(const SPIDev&) = delete;
+ SPIDev(SPIDev&&) = delete;
+ SPIDev& operator=(SPIDev&&) = delete;
+
+ SPIDev(const std::string& spiDev) :
+ fd(open(spiDev.c_str(), O_RDWR | O_CLOEXEC))
+ {
+ if (fd < 0)
+ {
+ std::string msg = "Unable to open mtd device. errno=" +
+ std::string(std::strerror(errno));
+ throw std::runtime_error(msg);
+ }
+ }
+
+ virtual ~SPIDev()
+ {
+ if (!(fd < 0))
+ {
+ close(fd);
+ }
+ }
+
+ void spiReadData(const uint32_t startAddr, const size_t dataLen,
+ void* dataRes)
+ {
+ if (lseek(fd, startAddr, SEEK_SET) < 0)
+ {
+ std::string msg = "Failed to do lseek on mtd device. errno=" +
+ std::string(std::strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ if (read(fd, dataRes, dataLen) != dataLen)
+ {
+ std::string msg = "Failed to read on mtd device. errno=" +
+ std::string(std::strerror(errno));
+ throw std::runtime_error(msg);
+ }
+
+ return;
+ }
+};
diff --git a/src/firmware-update.cpp b/src/firmware-update.cpp
index 09f6e90..b4c7608 100644
--- a/src/firmware-update.cpp
+++ b/src/firmware-update.cpp
@@ -26,12 +26,16 @@
#include <sdbusplus/server/object.hpp>
#include <sdbusplus/timer.hpp>
#include <sstream>
+#ifdef INTEL_PFR_ENABLED
+#include <spiDev.hpp>
+#endif
namespace ipmi
{
namespace firmware
{
constexpr Cmd cmdGetFwVersionInfo = 0x20;
+constexpr ipmi::Cmd cmdFwGetRootCertData = 0x25;
} // namespace firmware
} // namespace ipmi
@@ -1472,69 +1476,92 @@
return rc;
}
-struct fw_cert_data_req
+#ifdef INTEL_PFR_ENABLED
+enum class FwGetRootCertDataTag : uint8_t
{
- uint8_t cert_id;
- uint16_t offset;
- uint16_t count;
-} __attribute__((packed));
+ activeRootKey = 1,
+ recoveryRootKey,
+ activeCSK,
+ recoveryCSK,
+};
-static ipmi_ret_t ipmi_firmware_get_root_cert_data(
- ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
- ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t context)
+static constexpr char *bmcActivePfmMTDDev = "/dev/mtd/pfm";
+static constexpr char *bmcRecoveryImgMTDDev = "/dev/mtd/rc-image";
+static constexpr size_t pfmBaseOffsetInImage = 0x400;
+static constexpr size_t rootkeyOffsetInPfm = 0xA0;
+static constexpr size_t cskKeyOffsetInPfm = 0x124;
+static constexpr size_t cskSignatureOffsetInPfm = 0x19c;
+static constexpr size_t certKeyLen = 96;
+static constexpr size_t cskSignatureLen = 96;
+
+ipmi::RspType<std::array<uint8_t, certKeyLen>,
+ std::optional<std::array<uint8_t, cskSignatureLen>>>
+ ipmiGetFwRootCertData(uint8_t certId)
{
- if (DEBUG)
- std::cerr << "Get FW root cert data\n";
+ size_t certKeyOffset = 0;
+ size_t cskSigOffset = 0;
+ std::string mtdDev;
- // request:
- // Byte 1 - certificate ID: request which certificate (ignored)
- // Byte 2-3 - offset within cert to start at
- // Byte 4-5 - number of bytes to return
+ switch (static_cast<FwGetRootCertDataTag>(certId))
+ {
+ case FwGetRootCertDataTag::activeRootKey:
+ {
+ mtdDev = bmcActivePfmMTDDev;
+ certKeyOffset = rootkeyOffsetInPfm;
+ break;
+ }
+ case FwGetRootCertDataTag::recoveryRootKey:
+ {
+ mtdDev = bmcRecoveryImgMTDDev;
+ certKeyOffset = pfmBaseOffsetInImage + rootkeyOffsetInPfm;
+ break;
+ }
+ case FwGetRootCertDataTag::activeCSK:
+ {
+ mtdDev = bmcActivePfmMTDDev;
+ certKeyOffset = cskKeyOffsetInPfm;
+ cskSigOffset = cskSignatureOffsetInPfm;
+ break;
+ }
+ case FwGetRootCertDataTag::recoveryCSK:
+ {
+ mtdDev = bmcRecoveryImgMTDDev;
+ certKeyOffset = pfmBaseOffsetInImage + cskKeyOffsetInPfm;
+ cskSigOffset = pfmBaseOffsetInImage + cskSignatureOffsetInPfm;
+ break;
+ }
+ default:
+ {
+ return ipmi::responseInvalidFieldRequest();
+ }
+ }
- // response:
- // Byte 1-N - certificate data
+ std::array<uint8_t, certKeyLen> certKey = {0};
- if (*data_len != sizeof(fw_cert_data_req))
- return IPMI_CC_REQ_DATA_LEN_INVALID;
-
- auto cert_data_req = reinterpret_cast<struct fw_cert_data_req *>(request);
- std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
- auto method = bus->new_method_call(
- FW_UPDATE_SERVER_DBUS_NAME, FW_UPDATE_SERVER_INFO_PATH,
- "org.freedesktop.DBus.Properties", "Get");
- method.append(FW_UPDATE_SECURITY_INTERFACE, "certificate");
- ipmi::DbusVariant cert;
try
{
- auto reply = bus->call(method);
- reply.read(cert);
+ SPIDev spiDev(mtdDev);
+ spiDev.spiReadData(certKeyOffset, certKeyLen, certKey.data());
+
+ if (cskSigOffset)
+ {
+ std::array<uint8_t, cskSignatureLen> cskSignature = {0};
+ spiDev.spiReadData(cskSigOffset, cskSignatureLen,
+ cskSignature.data());
+ return ipmi::responseSuccess(certKey, cskSignature);
+ }
}
- catch (sdbusplus::exception::SdBusError &e)
+ catch (const std::exception &e)
{
- std::cerr << "SDBus Error: " << e.what();
- return IPMI_CC_UNSPECIFIED_ERROR;
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Exception caught in ipmiGetFwRootCertData",
+ phosphor::logging::entry("MSG=%s", e.what()));
+ return ipmi::responseUnspecifiedError();
}
- auto cert_data = std::get<std::string>(cert);
- if (cert_data_req->offset >= cert_data.size())
- {
- *data_len = 0;
- return IPMI_CC_INVALID_FIELD_REQUEST;
- }
- auto first = cert_data.begin() + cert_data_req->offset;
- auto last = first + cert_data_req->count;
- if (last > cert_data.end())
- last = cert_data.end();
-
- auto data_out = reinterpret_cast<char *>(response);
- std::copy(first, last, data_out);
-
- // Status code.
- ipmi_ret_t rc = IPMI_CC_OK;
- *data_len = (last - first);
-
- return rc;
+ return ipmi::responseSuccess(certKey, std::nullopt);
}
+#endif
static ipmi_ret_t ipmi_firmware_write_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
@@ -1640,7 +1667,6 @@
static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPD_CHAN_INFO = 0x22;
static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_BMC_EXEC_CTX = 0x23;
static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_INFO = 0x24;
-static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_ROOT_CERT_DATA = 0x25;
static constexpr ipmi_cmd_t IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM = 0x26;
static constexpr ipmi_cmd_t IPMI_CMD_FW_SET_FW_UPDATE_MODE = 0x27;
static constexpr ipmi_cmd_t cmdFirmwareExitFirmwareUpdateMode = 0x28;
@@ -1695,11 +1721,12 @@
ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_INFO,
NULL, ipmi_firmware_get_root_cert_info,
PRIVILEGE_ADMIN);
-
+#ifdef INTEL_PFR_ENABLED
// get root certificate data
- ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_ROOT_CERT_DATA,
- NULL, ipmi_firmware_get_root_cert_data,
- PRIVILEGE_ADMIN);
+ ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnFirmware,
+ ipmi::firmware::cmdFwGetRootCertData,
+ ipmi::Privilege::Admin, ipmiGetFwRootCertData);
+#endif
// generate bmc fw update random number (for enter fw tranfer mode)
ipmi_register_callback(NETFUN_FIRMWARE, IPMI_CMD_FW_GET_FW_UPDATE_RAND_NUM,