chromium/chrome/browser/ash/borealis/borealis_hardware_checker.cc

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

#include "chrome/browser/ash/borealis/borealis_hardware_checker.h"

#include "ash/constants/ash_features.h"
#include "base/cpu.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "third_party/re2/src/re2/re2.h"

namespace borealis {

namespace {

constexpr uint64_t kGibi = 1024ull * 1024 * 1024;

// Regex used for CPU checks on intel processors, this means "any i{3,5,7}
// processor". e.g.:
//  - Valid:   11th Gen Intel(R) Core(TM) i5-1145G7 @ 2.60GHz
//  - Valid:   Intel(R) Core(TM) 5 ...
//  - Invalid: Intel(R) Pentium(R) Gold 7505
constexpr char kIntelCpuRegex[] = "((i[357]-)|(Core.* [357]))";

// As above, for AMD processors, e.g. "AMD Ryzen 3 5125C with Radeon Graphics".
constexpr char kAmdCpuRegex[] = "Ryzen [357]";

std::string* g_cpu_brand_for_test_ = nullptr;

// Returns the Board's name according to /etc/lsb-release. Strips any variant
// except the "-borealis" variant.
//
// Note: the comment on GetLsbReleaseBoard() (rightly) points out that we're
// not supposed to use LsbReleaseBoard directly, but rather set a flag in
// the overlay. We are not doing that as the following check is only a
// temporary hack necessary while we release borealis, but will be removed
// shortly afterwards. This check can fail in either direction and we won't
// be too upset.
std::string GetBoardName() {
  // In a developer build, the name "volteer" or "volteer-borealis" will become
  // "volteer-signed-mp-blahblah" and "volteer-borealis-signed..." on a signed
  // build, so we want to stop everything after the "-" unless its "-borealis".
  //
  // This means a variant like "volteer-kernelnext" will be treated as "volteer"
  // by us.
  std::vector<std::string> pieces =
      base::SplitString(base::SysInfo::GetLsbReleaseBoard(), "-",
                        base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
  if (pieces.size() >= 2 && pieces[1] == "borealis") {
    return pieces[0] + "-" + pieces[1];
  }
  DCHECK(!pieces.empty());
  return pieces[0];
}

bool IsBoard(const std::string& board) {
  return GetBoardName() == board;
}

bool BoardIn(const base::flat_set<std::string>& boards) {
  return boards.contains(GetBoardName());
}

bool CpuRegexMatches(const std::string& cpu_regex) {
  return RE2::PartialMatch(
      g_cpu_brand_for_test_ ? *g_cpu_brand_for_test_
                            : base::CPU::GetInstanceNoAllocation().cpu_brand(),
      cpu_regex);
}

bool HasMemory(uint64_t mem_bytes) {
  return base::SysInfo::AmountOfPhysicalMemory() >= mem_bytes;
}

bool HasSufficientHardware(const std::string& cpu_regex) {
  return HasMemory(7 * kGibi) && CpuRegexMatches(cpu_regex);
}

bool InTargetSegment() {
  return base::FeatureList::IsEnabled(
      ash::features::kFeatureManagementBorealis);
}

bool Check() {
  if (BoardIn({"hatch", "drallion", "puff"})) {
    return HasSufficientHardware(kIntelCpuRegex);
  }
  if (IsBoard("volteer")) {
    return HasSufficientHardware(kIntelCpuRegex);
  }
  if (BoardIn({"brya", "adlrvp", "brask", "brox"})) {
    return HasSufficientHardware(kIntelCpuRegex);
  }
  if (BoardIn({"guybrush", "majolica"})) {
    return HasSufficientHardware(kAmdCpuRegex);
  }
  if (BoardIn({"aurora", "myst"})) {
    return true;
  }
  if (IsBoard("nissa")) {
    return HasSufficientHardware(kIntelCpuRegex) && InTargetSegment();
  }
  if (IsBoard("skyrim")) {
    return HasSufficientHardware(kAmdCpuRegex) && InTargetSegment();
  }
  if (IsBoard("rex")) {
    // TODO(307825451): .* allows any CPU, add the correct cpu regex once we
    // know what that is.
    return HasSufficientHardware(".*");
  }
  return false;
}

}  // namespace

void HasSufficientHardware(base::OnceCallback<void(bool)> callback) {
  ash::system::StatisticsProvider::GetInstance()
      ->ScheduleOnMachineStatisticsLoaded(base::BindOnce(
          [](base::OnceCallback<void(bool)> callback) {
            base::ThreadPool::PostTaskAndReplyWithResult(
                FROM_HERE, base::MayBlock(), base::BindOnce(&Check),
                std::move(callback));
          },
          std::move(callback)));
}

void SetCpuForTesting(std::string* cpu_brand) {
  g_cpu_brand_for_test_ = cpu_brand;
}

}  // namespace borealis