1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/debug/trace_event.h"
12 #include "base/message_loop.h"
13 #include "base/rand_util.h"
14 #include "base/string_number_conversions.h"
15 #include "base/stringprintf.h"
16 #include "base/threading/platform_thread.h"
17 #include "build/build_config.h"
18 #include "content/common/gpu/gpu_config.h"
19 #include "content/gpu/gpu_child_thread.h"
20 #include "content/gpu/gpu_info_collector.h"
21 #include "content/gpu/gpu_process.h"
22 #include "content/gpu/gpu_watchdog_thread.h"
23 #include "content/public/common/content_client.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/common/gpu_switching_option.h"
26 #include "content/public/common/main_function_params.h"
27 #include "crypto/hmac.h"
28 #include "ui/gl/gl_implementation.h"
29 #include "ui/gl/gl_surface.h"
30 #include "ui/gl/gl_switches.h"
31 #include "ui/gl/gpu_switching_manager.h"
34 #include "base/win/scoped_com_initializer.h"
35 #include "content/common/gpu/media/dxva_video_decode_accelerator.h"
36 #include "sandbox/win/src/sandbox.h"
37 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
38 #include "content/common/gpu/media/omx_video_decode_accelerator.h"
39 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
40 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
44 #include "ui/base/x/x11_util.h"
48 #include "content/public/common/sandbox_init.h"
51 const int kGpuTimeout
= 10000;
55 void WarmUpSandbox(const GPUInfo
&, bool);
58 // Main function for starting the Gpu process.
59 int GpuMain(const MainFunctionParams
& parameters
) {
60 TRACE_EVENT0("gpu", "GpuMain");
62 base::Time start_time
= base::Time::Now();
64 const CommandLine
& command_line
= parameters
.command_line
;
65 if (command_line
.HasSwitch(switches::kGpuStartupDialog
)) {
66 ChildProcess::WaitForDebugger("Gpu");
69 if (!command_line
.HasSwitch(switches::kSingleProcess
)) {
71 // Prevent Windows from displaying a modal dialog on failures like not being
72 // able to load a DLL.
74 SEM_FAILCRITICALERRORS
|
75 SEM_NOGPFAULTERRORBOX
|
76 SEM_NOOPENFILEERRORBOX
);
77 #elif defined(USE_X11)
78 ui::SetDefaultX11ErrorHandlers();
82 if (command_line
.HasSwitch(switches::kSupportsDualGpus
) &&
83 command_line
.HasSwitch(switches::kGpuSwitching
)) {
84 std::string option
= command_line
.GetSwitchValueASCII(
85 switches::kGpuSwitching
);
86 if (option
== switches::kGpuSwitchingOptionNameForceDiscrete
)
87 ui::GpuSwitchingManager::GetInstance()->ForceUseOfDiscreteGpu();
88 else if (option
== switches::kGpuSwitchingOptionNameForceIntegrated
)
89 ui::GpuSwitchingManager::GetInstance()->ForceUseOfIntegratedGpu();
92 // Initialization of the OpenGL bindings may fail, in which case we
93 // will need to tear down this process. However, we can not do so
94 // safely until the IPC channel is set up, because the detection of
95 // early return of a child process is implemented using an IPC
96 // channel error. If the IPC channel is not fully set up between the
97 // browser and GPU process, and the GPU process crashes or exits
98 // early, the browser process will never detect it. For this reason
99 // we defer tearing down the GPU process until receiving the
100 // GpuMsg_Initialize message from the browser.
101 bool dead_on_arrival
= false;
103 MessageLoop::Type message_loop_type
= MessageLoop::TYPE_IO
;
105 // Unless we're running on desktop GL, we don't need a UI message
106 // loop, so avoid its use to work around apparent problems with some
107 // third-party software.
108 if (command_line
.HasSwitch(switches::kUseGL
) &&
109 command_line
.GetSwitchValueASCII(switches::kUseGL
) ==
110 gfx::kGLImplementationDesktopName
) {
111 message_loop_type
= MessageLoop::TYPE_UI
;
113 #elif defined(OS_LINUX)
114 message_loop_type
= MessageLoop::TYPE_DEFAULT
;
117 MessageLoop
main_message_loop(message_loop_type
);
118 base::PlatformThread::SetName("CrGpuMain");
120 // In addition to disabling the watchdog if the command line switch is
121 // present, disable the watchdog on valgrind because the code is expected
122 // to run slowly in that case.
123 bool enable_watchdog
=
124 !CommandLine::ForCurrentProcess()->HasSwitch(
125 switches::kDisableGpuWatchdog
) &&
126 !RunningOnValgrind();
128 // Disable the watchdog in debug builds because they tend to only be run by
129 // developers who will not appreciate the watchdog killing the GPU process.
131 enable_watchdog
= false;
134 bool delayed_watchdog_enable
= false;
136 #if defined(OS_CHROMEOS)
137 // Don't start watchdog immediately, to allow developers to switch to VT2 on
139 delayed_watchdog_enable
= true;
142 scoped_refptr
<GpuWatchdogThread
> watchdog_thread
;
144 // Start the GPU watchdog only after anything that is expected to be time
145 // consuming has completed, otherwise the process is liable to be aborted.
146 if (enable_watchdog
&& !delayed_watchdog_enable
) {
147 watchdog_thread
= new GpuWatchdogThread(kGpuTimeout
);
148 watchdog_thread
->Start();
152 // Get vendor_id, device_id, driver_version from browser process through
153 // commandline switches.
154 DCHECK(command_line
.HasSwitch(switches::kGpuVendorID
) &&
155 command_line
.HasSwitch(switches::kGpuDeviceID
) &&
156 command_line
.HasSwitch(switches::kGpuDriverVersion
));
157 bool success
= base::HexStringToInt(
158 command_line
.GetSwitchValueASCII(switches::kGpuVendorID
),
159 reinterpret_cast<int*>(&(gpu_info
.gpu
.vendor_id
)));
161 success
= base::HexStringToInt(
162 command_line
.GetSwitchValueASCII(switches::kGpuDeviceID
),
163 reinterpret_cast<int*>(&(gpu_info
.gpu
.device_id
)));
165 gpu_info
.driver_vendor
=
166 command_line
.GetSwitchValueASCII(switches::kGpuDriverVendor
);
167 gpu_info
.driver_version
=
168 command_line
.GetSwitchValueASCII(switches::kGpuDriverVersion
);
169 GetContentClient()->SetGpuInfo(gpu_info
);
172 // Asynchronously initialize DXVA while GL is being initialized because
173 // they both take tens of ms.
174 base::WaitableEvent
dxva_initialized(true, false);
175 DXVAVideoDecodeAccelerator::PreSandboxInitialization(
176 base::Bind(&base::WaitableEvent::Signal
,
177 base::Unretained(&dxva_initialized
)));
180 // We need to track that information for the WarmUpSandbox function.
181 bool initialized_gl_context
= false;
182 // Load and initialize the GL implementation and locate the GL entry points.
183 if (gfx::GLSurface::InitializeOneOff()) {
184 if (!gpu_info_collector::CollectContextGraphicsInfo(&gpu_info
))
185 VLOG(1) << "gpu_info_collector::CollectGraphicsInfo failed";
186 GetContentClient()->SetGpuInfo(gpu_info
);
188 // We know that CollectGraphicsInfo will initialize a GLContext.
189 initialized_gl_context
= true;
191 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
192 if (gpu_info
.gpu
.vendor_id
== 0x10de && // NVIDIA
193 gpu_info
.driver_vendor
== "NVIDIA") {
194 base::ThreadRestrictions::AssertIOAllowed();
195 if (access("/dev/nvidiactl", R_OK
) != 0) {
196 VLOG(1) << "NVIDIA device file /dev/nvidiactl access denied";
197 gpu_info
.gpu_accessible
= false;
198 dead_on_arrival
= true;
201 #endif // OS_CHROMEOS
203 VLOG(1) << "gfx::GLSurface::InitializeOneOff failed";
204 gpu_info
.gpu_accessible
= false;
205 gpu_info
.finalized
= true;
206 dead_on_arrival
= true;
209 if (enable_watchdog
&& delayed_watchdog_enable
) {
210 watchdog_thread
= new GpuWatchdogThread(kGpuTimeout
);
211 watchdog_thread
->Start();
214 // OSMesa is expected to run very slowly, so disable the watchdog in that
216 if (enable_watchdog
&&
217 gfx::GetGLImplementation() == gfx::kGLImplementationOSMesaGL
) {
218 watchdog_thread
->Stop();
220 watchdog_thread
= NULL
;
224 const bool should_initialize_gl_context
= !initialized_gl_context
&&
226 // Warm up the current process before enabling the sandbox.
227 WarmUpSandbox(gpu_info
, should_initialize_gl_context
);
230 #if defined(OS_LINUX)
232 TRACE_EVENT0("gpu", "Initialize sandbox");
233 bool do_init_sandbox
= true;
235 #if defined(OS_CHROMEOS) && defined(NDEBUG)
236 // On Chrome OS and when not on a debug build, initialize
237 // the GPU process' sandbox only for Intel GPUs.
238 do_init_sandbox
= gpu_info
.gpu
.vendor_id
== 0x8086; // Intel GPU.
241 if (do_init_sandbox
) {
242 if (watchdog_thread
.get())
243 watchdog_thread
->Stop();
244 gpu_info
.sandboxed
= InitializeSandbox();
245 if (watchdog_thread
.get())
246 watchdog_thread
->Start();
253 // DXVA initialization must have completed before the token is lowered.
254 TRACE_EVENT0("gpu", "Wait for DXVA initialization");
255 dxva_initialized
.Wait();
259 TRACE_EVENT0("gpu", "Lower token");
260 // For windows, if the target_services interface is not zero, the process
261 // is sandboxed and we must call LowerToken() before rendering untrusted
263 sandbox::TargetServices
* target_services
=
264 parameters
.sandbox_info
->target_services
;
265 if (target_services
) {
266 target_services
->LowerToken();
267 gpu_info
.sandboxed
= true;
272 GpuProcess gpu_process
;
274 GpuChildThread
* child_thread
= new GpuChildThread(watchdog_thread
.get(),
275 dead_on_arrival
, gpu_info
);
277 child_thread
->Init(start_time
);
279 gpu_process
.set_main_thread(child_thread
);
282 TRACE_EVENT0("gpu", "Run Message Loop");
283 main_message_loop
.Run();
286 child_thread
->StopWatchdog();
293 #if defined(OS_LINUX)
294 void CreateDummyGlContext() {
295 scoped_refptr
<gfx::GLSurface
> surface(
296 gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1)));
297 if (!surface
.get()) {
298 VLOG(1) << "gfx::GLSurface::CreateOffscreenGLSurface failed";
302 // On Linux, this is needed to make sure /dev/nvidiactl has
303 // been opened and its descriptor cached.
304 scoped_refptr
<gfx::GLContext
> context(
305 gfx::GLContext::CreateGLContext(NULL
,
307 gfx::PreferDiscreteGpu
));
308 if (!context
.get()) {
309 VLOG(1) << "gfx::GLContext::CreateGLContext failed";
313 // Similarly, this is needed for /dev/nvidia0.
314 if (context
->MakeCurrent(surface
)) {
315 context
->ReleaseCurrent(surface
.get());
317 VLOG(1) << "gfx::GLContext::MakeCurrent failed";
322 void WarmUpSandbox(const GPUInfo
& gpu_info
,
323 bool should_initialize_gl_context
) {
325 TRACE_EVENT0("gpu", "Warm up rand");
326 // Warm up the random subsystem, which needs to be done pre-sandbox on all
328 (void) base::RandUint64();
331 TRACE_EVENT0("gpu", "Warm up HMAC");
332 // Warm up the crypto subsystem, which needs to done pre-sandbox on all
334 crypto::HMAC
hmac(crypto::HMAC::SHA256
);
335 unsigned char key
= '\0';
336 bool ret
= hmac
.Init(&key
, sizeof(key
));
340 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
341 OmxVideoDecodeAccelerator::PreSandboxInitialization();
342 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
343 VaapiVideoDecodeAccelerator::PreSandboxInitialization();
346 #if defined(OS_LINUX)
347 // We special case Optimus since the vendor_id we see may not be Nvidia.
348 bool uses_nvidia_driver
= (gpu_info
.gpu
.vendor_id
== 0x10de && // NVIDIA.
349 gpu_info
.driver_vendor
== "NVIDIA") ||
351 if (uses_nvidia_driver
&& should_initialize_gl_context
) {
352 // We need this on Nvidia to pre-open /dev/nvidiactl and /dev/nvidia0.
353 CreateDummyGlContext();
358 // Preload these DLL because the sandbox prevents them from loading.
359 LoadLibrary(L
"setupapi.dll");
365 } // namespace content