// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/power_metrics/smc_mac.h" #include #include #include "base/memory/ptr_util.h" namespace power_metrics { namespace { double FromSMCFixedPoint(uint8_t* bytes, size_t fraction_bits) { return OSReadBigInt16(bytes, 0) / static_cast(1 << fraction_bits); } } // namespace // static std::unique_ptr SMCReader::Create() { const base::mac::ScopedIOObject smc_service( IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC"))); base::mac::ScopedIOObject connect; if (IOServiceOpen(smc_service, mach_task_self(), 1, connect.InitializeInto()) != kIOReturnSuccess) { return nullptr; } return base::WrapUnique(new SMCReader(std::move(connect))); } SMCReader::~SMCReader() = default; absl::optional SMCReader::ReadKey(SMCKeyIdentifier identifier) { auto it = keys_.find(identifier); if (it == keys_.end()) { auto result = keys_.emplace(identifier, SMCKey(connect_, identifier)); it = result.first; } return it->second.Read(); } SMCReader::SMCKey::SMCKey(base::mac::ScopedIOObject connect, SMCKeyIdentifier key_identifier) : connect_(std::move(connect)), key_identifier_(key_identifier) { // Read key information. SMCParamStruct out{}; if (CallSMCFunction(kSMCGetKeyInfo, &out)) key_info_ = out.keyInfo; } SMCReader::SMCKey::SMCKey(SMCKey&&) = default; SMCReader::SMCKey& SMCReader::SMCKey::operator=(SMCKey&&) = default; SMCReader::SMCKey::~SMCKey() = default; bool SMCReader::SMCKey::Exists() const { return key_info_.dataSize > 0; } absl::optional SMCReader::SMCKey::Read() { if (!Exists()) return absl::nullopt; SMCParamStruct out{}; if (!CallSMCFunction(kSMCReadKey, &out)) return absl::nullopt; switch (key_info_.dataType) { case SMCDataType::flt: return *reinterpret_cast(out.bytes); case SMCDataType::sp78: return FromSMCFixedPoint(out.bytes, 8); case SMCDataType::sp87: return FromSMCFixedPoint(out.bytes, 7); case SMCDataType::spa5: return FromSMCFixedPoint(out.bytes, 5); default: return absl::nullopt; } } bool SMCReader::SMCKey::CallSMCFunction(uint8_t function, SMCParamStruct* out) { if (!connect_) return false; // TODO: In local tests, removing the calls to `kSMCUserClientOpen` and // `kSMCUserClientClose` doesn't seem to affect behavior. Consider removing // them. if (IOConnectCallMethod(connect_, kSMCUserClientOpen, nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr)) { connect_.reset(); return false; } SMCParamStruct in{}; in.key = key_identifier_; in.keyInfo.dataSize = key_info_.dataSize; in.data8 = function; size_t out_size = sizeof(*out); const bool success = IOConnectCallStructMethod(connect_, kSMCHandleYPCEvent, &in, sizeof(in), out, &out_size) == kIOReturnSuccess; if (IOConnectCallMethod(connect_, kSMCUserClientClose, nullptr, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr)) { connect_.reset(); } // Even if the close failed, report whether the actual call succeded. return success; } SMCReader::SMCReader(base::mac::ScopedIOObject connect) : connect_(std::move(connect)) {} } // namespace power_metrics