1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
// Copyright 2021 The Chromium Authors
// 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 <libkern/OSByteOrder.h>
#include <utility>
#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<double>(1 << fraction_bits);
}
} // namespace
// static
std::unique_ptr<SMCReader> SMCReader::Create() {
const base::mac::ScopedIOObject<io_service_t> smc_service(
IOServiceGetMatchingService(kIOMainPortDefault,
IOServiceMatching("AppleSMC")));
base::mac::ScopedIOObject<io_object_t> connect;
if (IOServiceOpen(smc_service.get(), mach_task_self(), 1,
connect.InitializeInto()) != kIOReturnSuccess) {
return nullptr;
}
return base::WrapUnique(new SMCReader(std::move(connect)));
}
SMCReader::~SMCReader() = default;
std::optional<double> 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<io_object_t> 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;
}
std::optional<double> SMCReader::SMCKey::Read() {
if (!Exists())
return std::nullopt;
SMCParamStruct out{};
if (!CallSMCFunction(kSMCReadKey, &out))
return std::nullopt;
switch (key_info_.dataType) {
case SMCDataType::flt:
return *reinterpret_cast<float*>(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 std::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_.get(), 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_.get(), kSMCHandleYPCEvent, &in,
sizeof(in), out, &out_size) == kIOReturnSuccess;
if (IOConnectCallMethod(connect_.get(), kSMCUserClientClose, nullptr, 0,
nullptr, 0, nullptr, nullptr, nullptr, nullptr)) {
connect_.reset();
}
// Even if the close failed, report whether the actual call succeeded.
return success;
}
SMCReader::SMCReader(base::mac::ScopedIOObject<io_object_t> connect)
: connect_(std::move(connect)) {}
} // namespace power_metrics
|