blob: 780741bd9b915500092b86df32ff323e9364ccc3 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_
#define TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_
#include <string.h>
#include <string>
#include "trunks/csme/pinweaver_csme_types.h"
#include <base/logging.h>
// TODO(b/190621192): Support variable length request and response, and further
// refactor all the consumers into one single template.
namespace trunks {
namespace csme {
template <typename Type>
std::string SerializeToString(const Type& t) {
const char* buffer = reinterpret_cast<const char*>(&t);
return std::string(buffer, buffer + sizeof(t));
}
// Declared as inline to temporarily avoid duplicated definition.
inline bool CheckResponse(const pw_heci_header_req& req_header,
const pw_heci_header_res& resp_header) {
if (req_header.pw_heci_seq != resp_header.pw_heci_seq) {
LOG(ERROR) << __func__ << ": Mismatched sequence: expected "
<< req_header.pw_heci_seq << " got " << resp_header.pw_heci_seq;
return false;
}
if (resp_header.pw_heci_rc) {
LOG(ERROR) << __func__
<< ": CSME returns error: " << resp_header.pw_heci_rc;
return false;
}
if (req_header.pw_heci_cmd != resp_header.pw_heci_cmd) {
LOG(ERROR) << __func__ << ": Mismatched command: expected "
<< req_header.pw_heci_cmd << " got " << resp_header.pw_heci_cmd;
return false;
}
return true;
}
// Implementation of deserialization of the packed data from CSME at recursion.
template <typename... OutputTypes>
class UnpackImpl;
// Unpacks the first data from the serialized payload, and invokes recursion.
template <typename FirstOutputType, typename... OutputTypes>
class UnpackImpl<FirstOutputType, OutputTypes...> {
public:
UnpackImpl() = default;
bool Unpack(const std::string& serialized,
FirstOutputType* first,
OutputTypes*... outputs) {
if (serialized.size() < sizeof(*first)) {
LOG(ERROR) << __func__ << ": Serialized data too short; expected >= "
<< sizeof(*first) << "; got " << serialized.size();
return false;
}
memcpy(first, serialized.data(), sizeof(*first));
return UnpackImpl<OutputTypes...>().Unpack(
serialized.substr(sizeof(*first)), outputs...);
}
};
// Unpacks the first data from the serialized payload, and invokes recursion.
#if 1
template <typename... OutputTypes>
class UnpackImpl<std::string, OutputTypes...> {
public:
UnpackImpl() = default;
bool Unpack(const std::string& serialized,
std::string* output_str,
OutputTypes*... outputs) {
// Report error as we don't have use case for empty string output.
if (serialized.empty()) {
LOG(ERROR) << __func__ << ": Expected empty string payload.";
return false;
}
*output_str = serialized;
// There isn't supposed to be any output parameters left; the recursive call
// will verify.
return UnpackImpl<OutputTypes...>().Unpack("", outputs...);
}
};
#endif
// Special handling for cases that all the output data are unpacked.
template <>
class UnpackImpl<> {
public:
UnpackImpl() = default;
bool Unpack(const std::string& serialized) {
if (!serialized.empty()) {
LOG(ERROR) << __func__ << ": Execessively long data; reminaing size="
<< serialized.size();
return false;
}
return true;
}
};
// Deserializes the response from CSME, including the integrity check against
// the CSME command.
template <typename... OutputTypes>
bool UnpackFromResponse(const pw_heci_header_req& req_header,
const std::string& response,
OutputTypes*... outputs) {
if (response.size() < sizeof(pw_heci_header_res)) {
LOG(ERROR) << __func__ << ": response too short; size=" << response.size();
return false;
}
const pw_heci_header_res* resp_header =
reinterpret_cast<const pw_heci_header_res*>(response.data());
if (!CheckResponse(req_header, *resp_header)) {
LOG(ERROR) << __func__ << ": Failed to vlaidate response header.";
return false;
}
const std::string serialized_outputs =
response.substr(sizeof(pw_heci_header_res));
if (resp_header->total_length != serialized_outputs.size()) {
LOG(ERROR) << __func__ << ": Unexpected payload length; specified: "
<< resp_header->total_length << " actual "
<< serialized_outputs.size();
return false;
}
if (!UnpackImpl<OutputTypes...>().Unpack(serialized_outputs, outputs...)) {
LOG(ERROR) << __func__ << ": Unpacking error.";
return false;
}
return true;
}
template <typename Type>
void BuildFixedSizedRequest(int cmd, int seq, Type* req) {
req->header.pw_heci_cmd = cmd;
req->header.pw_heci_seq = seq;
req->header.total_length = sizeof(Type) - sizeof(req->header);
}
} // namespace csme
} // namespace trunks
#endif // TRUNKS_CSME_PINWEAVER_CLIENT_UTILS_H_