blob: 05bd01ddea570eb2ff01609e7d30c3b0e024032e [file] [log] [blame]
// Copyright 2024 Google LLC
//
// 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.
#include "ec_util.hpp"
#include "google3/host_commands.h"
#include "host_command.hpp"
#include "message_util.hpp"
#include "payload_update.hpp"
#include <stdplus/print.hpp>
#include <stdplus/raw.hpp>
#include <xyz/openbmc_project/Control/Hoth/error.hpp>
#include <span>
#include <vector>
namespace google
{
namespace hoth
{
namespace internal
{
using sdbusplus::error::xyz::openbmc_project::control::hoth::ResponseFailure;
namespace
{
constexpr std::string_view kPanicDataMagic = "Pnc!";
constexpr uint32_t kPanicDataSize = 144;
bool matchPersistentPanicMagic(std::span<const uint8_t> response_body)
{
if (response_body.size() < kPanicDataSize)
{
return false;
}
return (response_body[kPanicDataSize - 4] == kPanicDataMagic[0] &&
response_body[kPanicDataSize - 3] == kPanicDataMagic[1] &&
response_body[kPanicDataSize - 2] == kPanicDataMagic[2] &&
response_body[kPanicDataSize - 1] == kPanicDataMagic[3]);
}
} // namespace
[[nodiscard]] std::span<const uint8_t>
EcUtilImpl::getResponseBody(std::vector<uint8_t> &response)
{
std::span<const uint8_t> output = response;
auto &rsp = stdplus::raw::extractRef<RspHeader>(output);
if (rsp.result != EC_RES_SUCCESS) {
stdplus::print(stderr, "Command received a bad response from Hoth {:#x}\n",
static_cast<uint8_t>(rsp.result));
throw ResponseFailure();
}
return output;
}
ec_response_statistics EcUtilImpl::getHothStatistics() const
{
std::vector<uint8_t> response = hostCmd->sendCommand(
EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_GET_STATISTICS,
kVersionZero, /*request=*/nullptr, /*requestSize=*/0);
std::span<const uint8_t> response_body = getResponseBody(response);
return stdplus::raw::copyFrom<ec_response_statistics>(response_body);
}
std::optional<ec_response_persistent_panic_info>
EcUtilImpl::getHothPersistentPanicInfo() const
{
// ec_response_persistent_panic_info is 6KiB. Declare the return value this
// way to leverage NRVO.
std::optional<ec_response_persistent_panic_info> panic;
panic.emplace();
std::span<uint8_t> panic_buf(reinterpret_cast<uint8_t*>(&panic.value()),
sizeof(panic.value()));
// The persistent panic info record is 6KiB long, so we have to retrieve it
// in chunks.
const size_t chunk_size = HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE;
const size_t num_chunks = sizeof(panic.value()) / chunk_size;
auto ptr = panic_buf.begin();
for (size_t i = 0; i < num_chunks; ++i, ptr += chunk_size)
{
ec_request_persistent_panic_info req = {
.operation = PERSISTENT_PANIC_INFO_GET,
.index = i,
};
std::vector<uint8_t> response = hostCmd->sendCommand(
EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
kVersionZero, &req, sizeof(req));
std::span<const uint8_t> response_body = getResponseBody(response);
if (response_body.size() != chunk_size)
{
stdplus::print(stderr, "Bad response length %d (expected %d)\n",
response_body.size(), chunk_size);
throw ResponseFailure();
}
// The first chunk should contain a panic magic in the last 4 bytes in
// the panic_record.
if (i == 0)
{
if (!matchPersistentPanicMagic(response_body))
{
panic.reset();
return panic;
}
}
std::copy(response_body.begin(), response_body.end(), ptr);
}
return panic;
}
bool EcUtilImpl::checkHothPersistentPanicInfo() const
{
ec_request_persistent_panic_info req = {
.operation = PERSISTENT_PANIC_INFO_GET,
.index = 0,
};
std::vector<uint8_t> response = hostCmd->sendCommand(
EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_PERSISTENT_PANIC_INFO,
kVersionZero, &req, sizeof(req));
std::span<const uint8_t> response_body = getResponseBody(response);
const size_t chunk_size = HOTH_PERSISTENT_PANIC_INFO_CHUNK_SIZE;
if (response_body.size() != chunk_size)
{
stdplus::print(stderr, "Bad response length %d (expected %d)\n",
response_body.size(), chunk_size);
throw ResponseFailure();
}
return matchPersistentPanicMagic(response_body);
}
} // namespace internal
} // namespace hoth
} // namespace google