blob: eebb85789e7cb7e41e2d053107f662737db4720a [file]
// 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 "content/utility/on_device_model/on_device_model_sandbox_init.h"
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/native_library.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#if defined(ENABLE_ML_INTERNAL)
#include "services/on_device_model/ml/chrome_ml.h" // nogncheck
#include "services/on_device_model/ml/gpu_blocklist.h" // nogncheck
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include <errno.h>
#include "content/common/gpu_pre_sandbox_hook_linux.h"
#include "gpu/config/gpu_info_collector.h" // nogncheck
#include "sandbox/policy/linux/sandbox_linux.h"
#endif
#if !BUILDFLAG(IS_FUCHSIA) && \
!(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER))
#include "base/feature_list.h"
#include "third_party/dawn/include/dawn/dawn_proc.h" // nogncheck
#include "third_party/dawn/include/dawn/native/DawnNative.h" // nogncheck
#include "third_party/dawn/include/dawn/webgpu_cpp.h" // nogncheck
#endif
namespace on_device_model {
namespace {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
constexpr uint32_t kVendorIdAMD = 0x1002;
constexpr uint32_t kVendorIdIntel = 0x8086;
constexpr uint32_t kVendorIdNVIDIA = 0x10DE;
constexpr uint32_t kVendorIdVirtIO = 0x1AF4;
void UpdateSandboxOptionsForGpu(
const gpu::GPUInfo::GPUDevice& device,
sandbox::policy::SandboxLinux::Options& options) {
switch (device.vendor_id) {
case kVendorIdAMD:
options.use_amd_specific_policies = true;
break;
case kVendorIdIntel:
options.use_intel_specific_policies = true;
break;
case kVendorIdNVIDIA:
options.use_nvidia_specific_policies = true;
break;
case kVendorIdVirtIO:
options.use_virtio_specific_policies = true;
break;
default:
break;
}
}
#endif
#if !BUILDFLAG(IS_FUCHSIA) && \
!(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER))
// If this feature is enabled, a WebGPU device is created for each valid
// adapter. This makes sure any relevant drivers or other libs are loaded before
// enabling the sandbox.
BASE_FEATURE(kOnDeviceModelWarmDrivers,
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
base::FEATURE_ENABLED_BY_DEFAULT
#else
base::FEATURE_DISABLED_BY_DEFAULT
#endif
);
#endif
bool ShouldWarmDrivers() {
#if BUILDFLAG(IS_FUCHSIA) || \
(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER))
return false;
#else
bool is_gpu_not_blocklisted = true;
#if defined(ENABLE_ML_INTERNAL)
ml::DeviceInfo device_info =
ml::QueryDeviceInfo(ml::ChromeML::Get()->api(), /*log_histogram=*/false);
is_gpu_not_blocklisted =
device_info.gpu_blocked_reason == ml::GpuBlockedReason::kNotBlocked;
#endif
return base::FeatureList::IsEnabled(kOnDeviceModelWarmDrivers) &&
is_gpu_not_blocklisted;
#endif
}
} // namespace
bool PreSandboxInit() {
#if defined(ENABLE_ML_INTERNAL)
// Ensure the library is loaded before the sandbox is initialized.
if (!ml::ChromeML::Get()) {
LOG(ERROR) << "Unable to load ChromeML.";
return false;
}
#endif
#if defined(DAWN_USE_BUILT_DXC) && BUILDFLAG(IS_WIN)
base::FilePath module_path;
if (base::PathService::Get(base::DIR_MODULE, &module_path)) {
// Preload DXC requirements if enabled.
base::LoadNativeLibrary(module_path.Append(L"dxil.dll"), nullptr);
base::LoadNativeLibrary(module_path.Append(L"dxcompiler.dll"), nullptr);
}
#endif
if (ShouldWarmDrivers()) {
// Warm any relevant drivers before attempting to bring up the sandbox. For
// good measure we initialize a device instance for any adapter with an
// appropriate backend on top of any integrated or discrete GPU.
#if !BUILDFLAG(IS_FUCHSIA) && \
!(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER))
dawnProcSetProcs(&dawn::native::GetProcs());
auto instance = std::make_unique<dawn::native::Instance>();
const wgpu::RequestAdapterOptions adapter_options{
#if BUILDFLAG(IS_WIN)
.backendType = wgpu::BackendType::D3D12,
#elif BUILDFLAG(IS_APPLE)
.backendType = wgpu::BackendType::Metal,
#else
.backendType = wgpu::BackendType::Vulkan,
#endif
};
std::vector<dawn::native::Adapter> adapters =
instance->EnumerateAdapters(&adapter_options);
for (auto& nativeAdapter : adapters) {
wgpu::Adapter adapter = wgpu::Adapter(nativeAdapter.Get());
wgpu::AdapterInfo info;
adapter.GetInfo(&info);
if (info.adapterType == wgpu::AdapterType::IntegratedGPU ||
info.adapterType == wgpu::AdapterType::DiscreteGPU) {
const wgpu::DeviceDescriptor descriptor;
wgpu::Device device{nativeAdapter.CreateDevice(&descriptor)};
if (device) {
device.Destroy();
}
}
}
#endif
}
return true;
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
void AddSandboxLinuxOptions(sandbox::policy::SandboxLinux::Options& options) {
// Make sure any necessary vendor-specific options are set.
gpu::GPUInfo info;
gpu::CollectBasicGraphicsInfo(&info);
UpdateSandboxOptionsForGpu(info.gpu, options);
for (const auto& gpu : info.secondary_gpus) {
UpdateSandboxOptionsForGpu(gpu, options);
}
}
bool PreSandboxHook(sandbox::policy::SandboxLinux::Options options) {
std::vector<sandbox::syscall_broker::BrokerFilePermission> file_permissions =
content::FilePermissionsForGpu(options);
file_permissions.push_back(
sandbox::syscall_broker::BrokerFilePermission::ReadOnly(
"/sys/devices/system/cpu/online"));
sandbox::policy::SandboxLinux::GetInstance()->StartBrokerProcess(
content::CommandSetForGPU(options), file_permissions, options);
if (!content::LoadLibrariesForGpu(options)) {
return false;
}
errno = 0;
return true;
}
#endif
bool Shutdown() {
return true;
}
} // namespace on_device_model