blob: 3f7ec9f7d1a9d3c0908469b05290dd165dfa4ab4 [file]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/drm/host/drm_device_handle.h"
#include <fcntl.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <utility>
#include "base/files/file_path.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
namespace ui {
namespace {
// Sleep this many milliseconds before retrying after authentication fails.
const int kAuthFailSleepMs = 100;
// Log a warning after failing to authenticate for this many milliseconds.
const int kLogAuthFailDelayMs = 1000;
bool Authenticate(int fd) {
drm_magic_t magic;
memset(&magic, 0, sizeof(magic));
// We need to make sure the DRM device has enough privilege. Use the DRM
// authentication logic to figure out if the device has enough permissions.
return !drmGetMagic(fd, &magic) && !drmAuthMagic(fd, magic);
}
} // namespace
DrmDeviceHandle::DrmDeviceHandle() {
}
DrmDeviceHandle::~DrmDeviceHandle() {
if (file_.is_valid())
base::AssertBlockingAllowedDeprecated();
}
bool DrmDeviceHandle::Initialize(const base::FilePath& dev_path,
const base::FilePath& sys_path) {
// Security folks have requested that we assert the graphics device has the
// expected path, so use a CHECK instead of a DCHECK. The sys_path is only
// used a label and is otherwise unvalidated.
CHECK(dev_path.DirName() == base::FilePath("/dev/dri"));
base::AssertBlockingAllowedDeprecated();
int num_auth_attempts = 0;
bool logged_warning = false;
const base::TimeTicks start_time = base::TimeTicks::Now();
while (true) {
file_.reset();
int fd = HANDLE_EINTR(open(dev_path.value().c_str(), O_RDWR | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "Failed to open " << dev_path.value();
return false;
}
file_.reset(fd);
sys_path_ = sys_path;
num_auth_attempts++;
if (Authenticate(file_.get())) {
struct drm_set_client_cap cap = {DRM_CLIENT_CAP_ATOMIC, 1};
has_atomic_capabilities_ =
!drmIoctl(file_.get(), DRM_IOCTL_SET_CLIENT_CAP, &cap);
cap = {DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1};
drmIoctl(file_.get(), DRM_IOCTL_SET_CLIENT_CAP, &cap);
break;
}
// To avoid spamming the logs, hold off before logging a warning (some
// failures are expected at first) and only log a single message.
if (!logged_warning &&
(base::TimeTicks::Now() - start_time).InMilliseconds() >=
kLogAuthFailDelayMs) {
LOG(WARNING) << "Failed to authenticate " << dev_path.value()
<< " within " << kLogAuthFailDelayMs << " ms";
logged_warning = true;
}
usleep(kAuthFailSleepMs * 1000);
}
VLOG(1) << "Succeeded authenticating " << dev_path.value() << " in "
<< (base::TimeTicks::Now() - start_time).InMilliseconds() << " ms "
<< "with " << num_auth_attempts << " attempt(s)";
return true;
}
bool DrmDeviceHandle::IsValid() const {
return file_.is_valid();
}
base::ScopedFD DrmDeviceHandle::PassFD() {
return std::move(file_);
}
} // namespace ui