| // 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 "hoth_state.hpp" |
| |
| #include "google3/host_commands.h" |
| |
| #include "ec_util.hpp" |
| |
| #include <stdplus/print.hpp> |
| #include <stdplus/raw.hpp> |
| #include <xyz/openbmc_project/Control/Hoth/State/error.hpp> |
| |
| #include <chrono> |
| #include <cstdint> |
| #include <cstring> |
| #include <exception> |
| #include <functional> |
| #include <future> |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| namespace |
| { |
| |
| constexpr std::chrono::minutes kTimerInterval = std::chrono::minutes(5); |
| |
| template <typename T> |
| bool statisticsReponseHasEnoughWords(const ec_response_statistics& stats, |
| const T& field) |
| { |
| uint64_t offset_required = |
| (uint64_t)&field - (uint64_t)&stats + sizeof(field); |
| return stats.valid_words * 4 >= offset_required; |
| } |
| } // namespace |
| |
| HothState::HothState(sdbusplus::bus::bus& bus, boost::asio::io_context& io, |
| const char* objPath, internal::HostCommand* hostCmd, |
| internal::EcUtil* ecutil, |
| const std::string& transport_type) : |
| HothStateObject(bus, objPath), bus(bus), io(io), hostCmd(hostCmd), |
| ecUtil(ecutil), timer(io) |
| { |
| // Update metrics immediately while kicking off the timer. |
| timerUpdateMetrics(); |
| |
| HothStateObject::transportType(transport_type); |
| } |
| |
| void HothState::updateMetrics() |
| { |
| try |
| { |
| statistics = ecUtil->getHothStatistics(); |
| |
| // Emit the signal that property has changed. |
| // Fall back to default values if response does not contain enought |
| // words. |
| HothStateObject::resetFlags( |
| statisticsReponseHasEnoughWords(statistics, |
| statistics.hoth_reset_flags) |
| ? (uint32_t)statistics.hoth_reset_flags |
| : 0); |
| HothStateObject::upTime( |
| statisticsReponseHasEnoughWords(statistics, |
| statistics.time_since_hoth_boot_us) |
| ? (uint64_t)statistics.time_since_hoth_boot_us |
| : 0); |
| HothStateObject::roInfoStrikes( |
| statisticsReponseHasEnoughWords(statistics, |
| statistics.ro_info_strikes) |
| ? (uint32_t)statistics.ro_info_strikes |
| : 0xffffffff); |
| HothStateObject::rwInfoStrikes( |
| statisticsReponseHasEnoughWords(statistics, |
| statistics.rw_info_strikes) |
| ? (uint32_t)statistics.rw_info_strikes |
| : 0xffffffff); |
| HothStateObject::payloadUpdateFailureCode( |
| statisticsReponseHasEnoughWords( |
| statistics, statistics.payload_update_failure_reason) |
| ? (uint16_t)statistics.payload_update_failure_reason |
| : 0xffff); |
| HothStateObject::payloadConfirmFailureCode( |
| statisticsReponseHasEnoughWords( |
| statistics, |
| statistics.payload_update_confirmation_cookie_failure_reason) |
| ? (uint32_t)statistics |
| .payload_update_confirmation_cookie_failure_reason |
| : 0xffffffff); |
| HothStateObject::firmwareUpdateFailureCode( |
| statisticsReponseHasEnoughWords( |
| statistics, statistics.firmware_update_failure_reason) |
| ? (uint16_t)statistics.firmware_update_failure_reason |
| : 0xffff); |
| HothStateObject::firmwareUpdateFailedMinor( |
| statisticsReponseHasEnoughWords( |
| statistics, statistics.failed_firmware_minor_version) |
| ? (uint32_t)statistics.failed_firmware_minor_version |
| : 0xffffffff); |
| HothStateObject::bootloaderUpdateFailureCode( |
| statisticsReponseHasEnoughWords(statistics, |
| statistics.bootloader_update_error) |
| ? (uint32_t)statistics.bootloader_update_error |
| : 0xffffffff); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, |
| "Fetching Hoth statistics had an exception: {}\n", |
| e.what()); |
| } |
| |
| try |
| { |
| ec_response_chip_info chip_info = ecUtil->getHothChipInfo(); |
| std::ostringstream serial_number; |
| serial_number << chip_info.hardware_category << "-"; |
| serial_number << std::setfill('0') << std::setw(16) << std::noshowbase |
| << std::hex << chip_info.hardware_identity; |
| HothStateObject::chipSerialNumber(serial_number.str()); |
| if (chip_info.hardware_category == 4) |
| { |
| HothStateObject::chipPartNumber("1100336"); |
| HothStateObject::chipRevision("H1D3P"); |
| } |
| else if (chip_info.hardware_category == 1 && |
| chip_info.info_variant == 1) |
| { |
| HothStateObject::chipPartNumber("1059594"); |
| HothStateObject::chipRevision("H1B3P"); |
| } |
| else if (chip_info.hardware_category == 1) |
| { |
| HothStateObject::chipPartNumber("07138244"); |
| HothStateObject::chipRevision("H1B2P"); |
| } |
| else |
| { |
| HothStateObject::chipPartNumber("unknown"); |
| HothStateObject::chipRevision("unknown"); |
| } |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Fetching Hoth chip info had an exception: {}\n", |
| e.what()); |
| } |
| |
| try |
| { |
| HothStateObject::hasPersistentPanicInfo( |
| ecUtil->checkHothPersistentPanicInfo()); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, |
| "Fetching Hoth panic info had an exception: {}\n", |
| e.what()); |
| } |
| |
| try |
| { |
| ec_authz_record_get_response auth_record_resp = |
| ecUtil->getHothAuthRecord(); |
| HothStateObject::hasValidAuthRecord(auth_record_resp.valid == 1); |
| if (auth_record_resp.valid == 1) |
| { |
| HothStateObject::authRecordCapabilities( |
| auth_record_resp.record.capabilities); |
| } |
| } |
| catch (const internal::CommandNotSupportedException& e) |
| { |
| // Do not log if unsupported |
| HothStateObject::hasValidAuthRecord(false); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, |
| "Fetching Hoth auth record had an exception: {}\n", |
| e.what()); |
| HothStateObject::hasValidAuthRecord(false); |
| } |
| |
| try |
| { |
| ec_response_key_rotation_status key_rotation_status = |
| ecUtil->getHothKeyRotationStatus(); |
| |
| HothStateObject::keyRotationVersion(key_rotation_status.version); |
| HothStateObject::keyRotationImageFamily( |
| key_rotation_status.image_family); |
| HothStateObject::keyRotationImageFamilyVariant( |
| key_rotation_status.image_family_variant); |
| HothStateObject::keyRotationValidationMethod( |
| key_rotation_status.validation_method); |
| HothStateObject::keyRotationValidationKeyData( |
| key_rotation_status.validation_key_data); |
| HothStateObject::keyRotationValidationHashData( |
| key_rotation_status.validation_hash_data); |
| } |
| catch (const internal::CommandNotSupportedException& e) |
| { |
| // Do not log if unsupported |
| HothStateObject::keyRotationValidationMethod( |
| KEY_ROTATION_VALIDATION_METHOD_GET_ERROR); |
| } |
| catch (const std::exception& e) |
| { |
| HothStateObject::keyRotationValidationMethod( |
| KEY_ROTATION_VALIDATION_METHOD_GET_ERROR); |
| stdplus::print(stderr, |
| "Fetching Hoth key rotation had an exception: {}\n", |
| e.what()); |
| } |
| |
| try |
| { |
| secure_boot_enforcement_state secure_boot_state = |
| ecUtil->getSecureBootEnforcementState(); |
| HothStateObject::secureBootEnforced(secure_boot_state.enabled != 0); |
| } |
| catch (const internal::CommandNotSupportedException& e) |
| { |
| // Do not log if unsupported |
| HothStateObject::secureBootEnforced(false); |
| } |
| catch (const std::exception& e) |
| { |
| HothStateObject::secureBootEnforced(false); |
| stdplus::print( |
| stderr, |
| "Fetching Hoth secure boot enforcement had an exception: {}\n", |
| e.what()); |
| } |
| |
| try |
| { |
| payload_status_response payload_status = ecUtil->getPayloadStatus(); |
| HothStateObject::payloadLockdownState( |
| payload_status.header.lockdown_state); |
| HothStateObject::payloadActivePartition( |
| payload_status.header.active_half); |
| if (payload_status.header.region_count >= 1) |
| { |
| HothStateObject::payloadValidationStateA( |
| payload_status.states[0].validation_state); |
| HothStateObject::payloadValidationFailureReasonA( |
| payload_status.states[0].failure_reason); |
| HothStateObject::payloadImageTypeA( |
| payload_status.states[0].image_type); |
| HothStateObject::payloadKeyIndexA( |
| payload_status.states[0].key_index); |
| HothStateObject::payloadImageFamilyA( |
| payload_status.states[0].image_family); |
| HothStateObject::payloadVersionA( |
| internal::payloadVersionString(payload_status.states[0])); |
| } |
| if (payload_status.header.region_count >= 2) |
| { |
| HothStateObject::payloadValidationStateB( |
| payload_status.states[1].validation_state); |
| HothStateObject::payloadValidationFailureReasonB( |
| payload_status.states[1].failure_reason); |
| HothStateObject::payloadImageTypeB( |
| payload_status.states[1].image_type); |
| HothStateObject::payloadKeyIndexB( |
| payload_status.states[1].key_index); |
| HothStateObject::payloadImageFamilyB( |
| payload_status.states[1].image_family); |
| HothStateObject::payloadVersionB( |
| internal::payloadVersionString(payload_status.states[1])); |
| } |
| } |
| catch (const internal::CommandNotSupportedException& e) |
| { |
| // Do not log if unsupported |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, |
| "Fetching Hoth payload status had an exception: {}\n", |
| e.what()); |
| } |
| } |
| |
| void HothState::timerUpdateMetrics() |
| { |
| try |
| { |
| updateMetrics(); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Fetching Hoth metrics had an exception: {}\n", |
| e.what()); |
| } |
| timer.expires_after(kTimerInterval); |
| // NOLINTNEXTLINE(modernize-avoid-bind) |
| timer.async_wait(std::bind(&HothState::timerUpdateMetrics, this)); |
| } |
| |
| } // namespace hoth |
| |
| } // namespace google |