chromium/base/cpu.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/cpu.h"

#include <stdint.h>
#include <string.h>

#include <string>
#include <string_view>
#include <utility>

#include "base/containers/span.h"
#include "base/containers/span_writer.h"
#include "base/memory/protected_memory.h"
#include "build/build_config.h"

#if defined(ARCH_CPU_ARM_FAMILY) && \
    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
#include <asm/hwcap.h>
#include <sys/auxv.h>

#include "base/files/file_util.h"
#include "base/numerics/checked_math.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"

// Temporary definitions until a new hwcap.h is pulled in everywhere.
// https://crbug.com/1265965
#ifndef HWCAP2_MTE
#define HWCAP2_MTE
#define HWCAP2_BTI
#endif

struct ProcCpuInfo {
  std::string brand;
  uint8_t implementer = 0;
  uint32_t part_number = 0;
};
#endif

#if defined(ARCH_CPU_X86_FAMILY)
#if defined(COMPILER_MSVC)
#include <intrin.h>
#include <immintrin.h>  // For _xgetbv()
#endif
#endif

namespace base {

#if defined(ARCH_CPU_X86_FAMILY)
namespace internal {

X86ModelInfo ComputeX86FamilyAndModel(const std::string& vendor,
                                      int signature) {}

}  // namespace internal
#endif  // defined(ARCH_CPU_X86_FAMILY)

CPU::CPU(bool require_branding) {}
CPU::CPU() :{}
CPU::CPU(CPU&&) = default;

namespace {

#if defined(ARCH_CPU_X86_FAMILY)
#if !defined(COMPILER_MSVC)

#if defined(__pic__) && defined(__i386__)

// Requests extended feature information via |ecx|.
void __cpuidex(int cpu_info[4], int eax, int ecx) {
  // SAFETY: `cpu_info` has length 4 and therefore all accesses below are valid.
  UNSAFE_BUFFERS(
      __asm__ volatile("mov %%ebx, %%edi\n"
                       "cpuid\n"
                       "xchg %%edi, %%ebx\n"
                       : "=a"(cpu_info[0]), "=D"(cpu_info[1]),
                         "=c"(cpu_info[2]), "=d"(cpu_info[3])
                       : "a"(eax), "c"(ecx)));
}

void __cpuid(int cpu_info[4], int info_type) {
  __cpuidex(cpu_info, info_type, /*ecx=*/0);
}

#else

// Requests extended feature information via |ecx|.
void __cpuidex(int cpu_info[4], int eax, int ecx) {}

void __cpuid(int cpu_info[4], int info_type) {}

#endif
#endif  // !defined(COMPILER_MSVC)

// xgetbv returns the value of an Intel Extended Control Register (XCR).
// Currently only XCR0 is defined by Intel so |xcr| should always be zero.
uint64_t xgetbv(uint32_t xcr) {}

#endif  // ARCH_CPU_X86_FAMILY

#if defined(ARCH_CPU_ARM_FAMILY) && \
    (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
StringPairs::const_iterator FindFirstProcCpuKey(const StringPairs& pairs,
                                                std::string_view key) {
  return ranges::find_if(pairs, [key](const StringPairs::value_type& pair) {
    return TrimWhitespaceASCII(pair.first, base::TRIM_ALL) == key;
  });
}

// Parses information about the ARM processor. Note that depending on the CPU
// package, processor configuration, and/or kernel version, this may only
// report information about the processor on which this thread is running. This
// can happen on heterogeneous-processor SoCs like Snapdragon 808, which has 4
// Cortex-A53 and 2 Cortex-A57. Unfortunately there is not a universally
// reliable way to examine the CPU part information for all cores.
const ProcCpuInfo& ParseProcCpu() {
  static const NoDestructor<ProcCpuInfo> info([]() {
    // This function finds the value from /proc/cpuinfo under the key "model
    // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
    // and later for arm64) and is shown once per CPU. "Processor" is used in
    // earler versions and is shown only once at the top of /proc/cpuinfo
    // regardless of the number CPUs.
    const char kModelNamePrefix[] = "model name";
    const char kProcessorPrefix[] = "Processor";

    std::string cpuinfo;
    ReadFileToString(FilePath("/proc/cpuinfo"), &cpuinfo);
    DCHECK(!cpuinfo.empty());

    ProcCpuInfo info;

    StringPairs pairs;
    if (!SplitStringIntoKeyValuePairs(cpuinfo, ':', '\n', &pairs)) {
      NOTREACHED();
    }

    auto model_name = FindFirstProcCpuKey(pairs, kModelNamePrefix);
    if (model_name == pairs.end())
      model_name = FindFirstProcCpuKey(pairs, kProcessorPrefix);
    if (model_name != pairs.end()) {
      TrimWhitespaceASCII(model_name->second, TRIM_ALL, &info.brand);
    }

    auto implementer_string = FindFirstProcCpuKey(pairs, "CPU implementer");
    if (implementer_string != pairs.end()) {
      // HexStringToUInt() handles the leading whitespace on the value.
      uint32_t implementer;
      HexStringToUInt(implementer_string->second, &implementer);
      if (!CheckedNumeric<uint32_t>(implementer)
               .AssignIfValid(&info.implementer)) {
        info.implementer = 0;
      }
    }

    auto part_number_string = FindFirstProcCpuKey(pairs, "CPU part");
    if (part_number_string != pairs.end())
      HexStringToUInt(part_number_string->second, &info.part_number);

    return info;
  }());

  return *info;
}
#endif  // defined(ARCH_CPU_ARM_FAMILY) && (BUILDFLAG(IS_ANDROID) ||
        // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))

DEFINE_PROTECTED_DATA base::ProtectedMemory<CPU> g_cpu_instance;

}  // namespace

void CPU::Initialize(bool require_branding) {}

#if defined(ARCH_CPU_X86_FAMILY)
CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const {}
#endif

const CPU& CPU::GetInstanceNoAllocation() {}

}  // namespace base