From 4e1d4303af0d507fdef77589f1431fc4266caccf Mon Sep 17 00:00:00 2001 From: ikalco <73481042+ikalco@users.noreply.github.com> Date: Sun, 14 Jul 2024 23:39:03 -0500 Subject: [PATCH 1/2] handle monitor hotplugs and fix a few bugs that it revealed --- include/aquamarine/backend/DRM.hpp | 2 +- src/backend/Session.cpp | 75 +++++++++++++++--------------- src/backend/drm/DRM.cpp | 29 ++++++++---- 3 files changed, 60 insertions(+), 46 deletions(-) diff --git a/include/aquamarine/backend/DRM.hpp b/include/aquamarine/backend/DRM.hpp index 5eaf7ed..88ec1d3 100644 --- a/include/aquamarine/backend/DRM.hpp +++ b/include/aquamarine/backend/DRM.hpp @@ -287,7 +287,7 @@ namespace Aquamarine { SDRMPageFlip pendingPageFlip; bool frameEventScheduled = false; - drmModeModeInfo fallbackModeInfo; + Hyprutils::Memory::CSharedPointer fallbackMode; struct { bool vrrEnabled = false; diff --git a/src/backend/Session.cpp b/src/backend/Session.cpp index ac2a5a4..5808ec0 100644 --- a/src/backend/Session.cpp +++ b/src/backend/Session.cpp @@ -303,46 +303,47 @@ void Aquamarine::CSession::dispatchUdevEvents() { return; } + dev_t deviceNum = udev_device_get_devnum(device); + SP sessionDevice; + for (auto& sDev : sessionDevices) { + if (sDev->dev == deviceNum) + sessionDevice = sDev; + } + + if (!sessionDevice) { + udev_device_unref(device); + return; + } + if (action == std::string{"add"}) events.addDrmCard.emit(SAddDrmCardEvent{.path = devnode}); - else if (action == std::string{"change"} || action == std::string{"remove"}) { - dev_t deviceNum = udev_device_get_devnum(device); - - for (auto& d : sessionDevices) { - if (d->dev != deviceNum) - continue; - - if (action == std::string{"change"}) { - backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} changed", sysname ? sysname : "unknown")); - - CSessionDevice::SChangeEvent event; - - auto prop = udev_device_get_property_value(device, "HOTPLUG"); - if (prop && prop == std::string{"1"}) { - event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG; - - prop = udev_device_get_property_value(device, "CONNECTOR"); - if (prop) - event.hotplug.connectorID = std::stoull(prop); - - prop = udev_device_get_property_value(device, "PROPERTY"); - if (prop) - event.hotplug.propID = std::stoull(prop); - } else if (prop = udev_device_get_property_value(device, "LEASE"); prop && prop == std::string{"1"}) { - event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE; - } else { - backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} change event unrecognized", sysname ? sysname : "unknown")); - break; - } - - d->events.change.emit(event); - } else if (action == std::string{"remove"}) { - backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} removed", sysname ? sysname : "unknown")); - d->events.remove.emit(); - } - - break; + else if (action == std::string{"change"}) { + backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} changed", sysname ? sysname : "unknown")); + + CSessionDevice::SChangeEvent event; + + // + auto prop = udev_device_get_property_value(device, "HOTPLUG"); + if (prop && prop == std::string{"1"}) { + event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG; + + prop = udev_device_get_property_value(device, "CONNECTOR"); + if (prop) + event.hotplug.connectorID = std::stoull(prop); + + prop = udev_device_get_property_value(device, "PROPERTY"); + if (prop) + event.hotplug.propID = std::stoull(prop); + } else if (prop = udev_device_get_property_value(device, "LEASE"); prop && prop == std::string{"1"}) { + event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE; + } else { + backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} change event unrecognized", sysname ? sysname : "unknown")); } + + sessionDevice->events.change.emit(event); + } else if (action == std::string{"remove"}) { + backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} removed", sysname ? sysname : "unknown")); + sessionDevice->events.remove.emit(); } udev_device_unref(device); diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 44e4dab..d5faf74 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -238,6 +238,9 @@ std::vector> Aquamarine::CDRMBackend::attempt(SP backe } backends.emplace_back(drmBackend); + + // so that session can handle udev change/remove events for this gpu + backend->session->sessionDevices.push_back(gpu); } return backends; @@ -793,6 +796,7 @@ void Aquamarine::CDRMBackend::onReady() { // swapchain has to be created here because allocator is absent in connect if not ready c->output->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock()); c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true, .multigpu = !!primary}); // mark the swapchain for scanout + c->output->setCursor(nullptr, {}); c->output->needsFrame = true; backend->events.newOutput.emit(SP(c->output)); @@ -1024,7 +1028,6 @@ drmModeModeInfo* Aquamarine::SDRMConnector::getCurrentMode() { if (crtc->props.mode_id) { size_t size = 0; return (drmModeModeInfo*)getDRMPropBlob(backend->gpu->fd, crtc->id, crtc->props.mode_id, &size); - ; } auto drmCrtc = drmModeGetCrtc(backend->gpu->fd, crtc->id); @@ -1095,15 +1098,15 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) { continue; } - if (i == 1) - fallbackModeInfo = drmMode; - auto aqMode = makeShared(); aqMode->pixelSize = {drmMode.hdisplay, drmMode.vdisplay}; aqMode->refreshRate = calculateRefresh(drmMode); aqMode->preferred = (drmMode.type & DRM_MODE_TYPE_PREFERRED); aqMode->modeInfo = drmMode; + if (i == 1) + fallbackMode = aqMode; + output->modes.emplace_back(aqMode); if (currentModeInfo && std::memcmp(&drmMode, currentModeInfo, sizeof(drmModeModeInfo))) { @@ -1120,6 +1123,11 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) { aqMode->preferred ? " (preferred)" : "")); } + if (!currentModeInfo) { + output->state->setMode(fallbackMode); + crtc->refresh = calculateRefresh(fallbackMode->modeInfo.value()); + } + output->physicalSize = {(double)connector->mmWidth, (double)connector->mmHeight}; backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Physical size {} (mm)", output->physicalSize)); @@ -1181,7 +1189,8 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) { return; output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock()); - backend->backend->events.newOutput.emit(output); + output->setCursor(nullptr, {}); + backend->backend->events.newOutput.emit(SP(output)); output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR); } @@ -1395,7 +1404,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { else data.cursorFB = connector->crtc->cursor->front; - if (data.cursorFB) { + if (data.cursorFB && data.cursorFB->buffer) { // verify cursor format. This might be wrong on NVIDIA where linear buffers // fail to be created from gbm // TODO: add an API to detect this and request drm_dumb linear buffers. Or do something, @@ -1439,9 +1448,13 @@ bool Aquamarine::CDRMOutput::setCursor(SP buffer, const Vector2D& hotsp return false; } - if (!buffer) + if (!buffer) { + connector->crtc->pendingCursor.reset(); + connector->crtc->cursor->front.reset(); + connector->crtc->cursor->back.reset(); + connector->crtc->cursor->last.reset(); setCursorVisible(false); - else { + } else { SP fb; if (backend->primary) { From 1e6acce6ee8562d5e4fd634af58fa559c99ac8ee Mon Sep 17 00:00:00 2001 From: ikalco <73481042+ikalco@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:11:50 -0500 Subject: [PATCH 2/2] fix cursorFB uaf and other stuf --- src/backend/Session.cpp | 4 +++- src/backend/drm/DRM.cpp | 14 ++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/backend/Session.cpp b/src/backend/Session.cpp index 5808ec0..33a428b 100644 --- a/src/backend/Session.cpp +++ b/src/backend/Session.cpp @@ -306,8 +306,10 @@ void Aquamarine::CSession::dispatchUdevEvents() { dev_t deviceNum = udev_device_get_devnum(device); SP sessionDevice; for (auto& sDev : sessionDevices) { - if (sDev->dev == deviceNum) + if (sDev->dev == deviceNum) { sessionDevice = sDev; + break; + } } if (!sessionDevice) { diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index d5faf74..f90967d 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -796,7 +796,6 @@ void Aquamarine::CDRMBackend::onReady() { // swapchain has to be created here because allocator is absent in connect if not ready c->output->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock()); c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true, .multigpu = !!primary}); // mark the swapchain for scanout - c->output->setCursor(nullptr, {}); c->output->needsFrame = true; backend->events.newOutput.emit(SP(c->output)); @@ -1189,7 +1188,6 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) { return; output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock()); - output->setCursor(nullptr, {}); backend->backend->events.newOutput.emit(SP(output)); output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR); } @@ -1404,12 +1402,12 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) { else data.cursorFB = connector->crtc->cursor->front; - if (data.cursorFB && data.cursorFB->buffer) { + if (data.cursorFB) { // verify cursor format. This might be wrong on NVIDIA where linear buffers // fail to be created from gbm // TODO: add an API to detect this and request drm_dumb linear buffers. Or do something, // idk - if (data.cursorFB->buffer->dmabuf().modifier == DRM_FORMAT_MOD_INVALID) { + if (data.cursorFB->dead || data.cursorFB->buffer->dmabuf().modifier == DRM_FORMAT_MOD_INVALID) { TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Dropping invalid buffer for cursor plane")); data.cursorFB = nullptr; } @@ -1448,13 +1446,9 @@ bool Aquamarine::CDRMOutput::setCursor(SP buffer, const Vector2D& hotsp return false; } - if (!buffer) { - connector->crtc->pendingCursor.reset(); - connector->crtc->cursor->front.reset(); - connector->crtc->cursor->back.reset(); - connector->crtc->cursor->last.reset(); + if (!buffer) setCursorVisible(false); - } else { + else { SP fb; if (backend->primary) {