wlroots-hyprland/backend/drm/backend.c

223 lines
6.2 KiB
C
Raw Normal View History

2018-02-12 21:29:23 +01:00
#include <assert.h>
#include <errno.h>
2017-05-01 07:49:18 +02:00
#include <stdio.h>
2018-02-12 21:29:23 +01:00
#include <stdlib.h>
2017-05-02 03:00:25 +02:00
#include <string.h>
2018-02-12 21:29:23 +01:00
#include <unistd.h>
#include <wayland-server-core.h>
2017-06-05 01:30:37 +02:00
#include <wlr/backend/interface.h>
2018-02-12 21:29:23 +01:00
#include <wlr/backend/session.h>
2017-06-21 16:27:45 +02:00
#include <wlr/interfaces/wlr_output.h>
2018-02-12 21:29:23 +01:00
#include <wlr/render/egl.h>
2017-10-22 04:00:04 +02:00
#include <wlr/types/wlr_list.h>
2017-06-21 18:10:07 +02:00
#include <wlr/util/log.h>
2018-02-12 21:29:23 +01:00
#include <xf86drm.h>
#include "backend/drm/drm.h"
2018-02-12 21:29:23 +01:00
#include "util/signal.h"
2017-05-01 07:49:18 +02:00
2018-09-17 22:25:20 +02:00
struct wlr_drm_backend *get_drm_backend_from_backend(
struct wlr_backend *wlr_backend) {
assert(wlr_backend_is_drm(wlr_backend));
return (struct wlr_drm_backend *)wlr_backend;
}
static bool backend_start(struct wlr_backend *backend) {
2018-09-17 22:25:20 +02:00
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
scan_drm_connectors(drm);
2017-05-07 16:00:23 +02:00
return true;
}
2017-05-03 11:28:44 +02:00
static void backend_destroy(struct wlr_backend *backend) {
2017-09-30 11:22:26 +02:00
if (!backend) {
2017-05-07 16:00:23 +02:00
return;
}
2018-09-17 22:25:20 +02:00
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
2017-09-30 11:22:26 +02:00
restore_drm_outputs(drm);
struct wlr_drm_connector *conn, *next;
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
wlr_output_destroy(&conn->output);
2017-05-31 22:17:04 +02:00
}
2017-08-05 08:15:39 +02:00
2018-02-12 09:12:31 +01:00
wlr_signal_emit_safe(&backend->events.destroy, backend);
2017-12-07 23:44:59 +01:00
wl_list_remove(&drm->display_destroy.link);
backend/drm, backend/libinput: listen to session destroy This fixes a heap-use-after-free when the session is destroyed before the backend during wl_display_destroy: ==1085==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000180 at pc 0x7f88e3590c2d bp 0x7ffdc4e33f90 sp 0x7ffdc4e33f80 READ of size 8 at 0x614000000180 thread T0 #0 0x7f88e3590c2c in find_device ../subprojects/wlroots/backend/session/session.c:192 #1 0x7f88e3590e85 in wlr_session_close_file ../subprojects/wlroots/backend/session/session.c:204 #2 0x7f88e357b80c in libinput_close_restricted ../subprojects/wlroots/backend/libinput/backend.c:24 #3 0x7f88e21af274 (/lib64/libinput.so.10+0x28274) #4 0x7f88e21aff1d (/lib64/libinput.so.10+0x28f1d) #5 0x7f88e219ddac (/lib64/libinput.so.10+0x16dac) #6 0x7f88e21b415d in libinput_unref (/lib64/libinput.so.10+0x2d15d) #7 0x7f88e357c9d6 in backend_destroy ../subprojects/wlroots/backend/libinput/backend.c:130 #8 0x7f88e3545a09 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:50 #9 0x7f88e358981a in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:54 #10 0x7f88e358a059 in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:107 #11 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) #12 0x7f88e314b466 in wl_display_destroy (/lib64/libwayland-server.so.0+0x9466) #13 0x559fefb52385 in main ../main.c:67 #14 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152) #15 0x559fefb4297d in _start (/home/simon/src/glider/build/glider+0x2297d) 0x614000000180 is located 320 bytes inside of 416-byte region [0x614000000040,0x6140000001e0) freed by thread T0 here: #0 0x7f88e3d0a6b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122 #1 0x7f88e35b51fb in logind_session_destroy ../subprojects/wlroots/backend/session/logind.c:270 #2 0x7f88e35905a4 in wlr_session_destroy ../subprojects/wlroots/backend/session/session.c:156 #3 0x7f88e358f440 in handle_display_destroy ../subprojects/wlroots/backend/session/session.c:65 #4 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) previously allocated by thread T0 here: #0 0x7f88e3d0acd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153 #1 0x7f88e35b911c in logind_session_create ../subprojects/wlroots/backend/session/logind.c:746 #2 0x7f88e358f6b4 in wlr_session_create ../subprojects/wlroots/backend/session/session.c:91 #3 0x559fefb51ea6 in main ../main.c:20 #4 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152)
2019-11-30 11:57:37 +01:00
wl_list_remove(&drm->session_destroy.link);
2017-12-07 23:44:59 +01:00
wl_list_remove(&drm->session_signal.link);
wl_list_remove(&drm->drm_invalidated.link);
finish_drm_resources(drm);
finish_drm_renderer(&drm->renderer);
2017-09-30 11:22:26 +02:00
wlr_session_close_file(drm->session, drm->fd);
wl_event_source_remove(drm->drm_event);
free(drm);
2017-05-07 16:00:23 +02:00
}
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *backend) {
2018-09-17 22:25:20 +02:00
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
if (drm->parent) {
return drm->parent->renderer.wlr_rend;
} else {
return drm->renderer.wlr_rend;
}
}
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
2018-10-01 22:44:33 +02:00
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
return drm->clock;
}
2017-05-07 16:00:23 +02:00
static struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.get_presentation_clock = backend_get_presentation_clock,
2017-05-07 16:00:23 +02:00
};
bool wlr_backend_is_drm(struct wlr_backend *b) {
return b->impl == &backend_impl;
}
static void session_signal(struct wl_listener *listener, void *data) {
2017-12-07 23:44:59 +01:00
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, session_signal);
struct wlr_session *session = data;
if (session->active) {
2018-07-09 23:49:54 +02:00
wlr_log(WLR_INFO, "DRM fd resumed");
scan_drm_connectors(drm);
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->outputs, link){
if (conn->output.enabled && conn->output.current_mode != NULL) {
drm_connector_set_mode(&conn->output,
conn->output.current_mode);
} else {
enable_drm_connector(&conn->output, false);
2017-10-22 23:44:24 +02:00
}
2017-08-09 10:43:01 +02:00
if (!conn->crtc) {
2017-08-09 15:43:42 +02:00
continue;
}
struct wlr_drm_plane *plane = conn->crtc->cursor;
drm->iface->crtc_set_cursor(drm, conn->crtc,
Allow cursor render surface to be used as fb In order for a surface to be used as a cursor plane framebuffer, it appears that requiring the buffer to be linear is sufficient. GBM_BO_USE_SCANOUT is added in case GBM_BO_USE_LINEAR isn't sufficient on untested hardware. Fixes #1323 Removed wlr_drm_plane.cursor_bo as it does not serve any purpose anymore. Relevant analysis (taken from the PR description): While trying to implement a fix for #1323, I found that when exporting the rendered surface into a DMA-BUF and reimporting it with `GBM_BO_USE_CURSOR`, the resulting object does not appear to be valid. After some digging (turning on drm-kms debugging and switching to legacy mode), I managed to extract the following error: ``` [drm:__setplane_check.isra.1 [drm]] Invalid pixel format AR24 little-endian (0x34325241), modifier 0x100000000000001 ``` The format itself refers to ARGB8888 which is the same format as `renderer->gbm_format` used in master to create the cursor bo. However, using `gbm_bo_create` with `GBM_BO_USE_CURSOR` results in a modifier of 0. A modifier of zero represents a linear buffer while the modifier of the surface that is rendered to is `I915_FORMAT_MOD_X_TILED` (see https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/uapi/drm/drm_fourcc.h?h=v4.20.6#n263). In order to fix this mismatch in modifier, I added the `GBM_BO_USE_LINEAR` to the render surface and everything started to work just fine. I wondered however, whether the export and import is really necessary. I then decided to test if the back buffer of the render surface works as well, and at least on my hardware (Intel HD 530 and Intel UHD 620) it does. This is the patch in this PR and this requires no exporting and importing. I have to note that I cheated in order to import DMA_BUFs into a cursor bo when doing the first tests, since on import the Intel drivers check that the cursor is 64x64. This is strange since cursor sizes other than 64x64 have been around for quite some time now (https://lists.freedesktop.org/archives/mesa-commit/2014-June/050268.html). Removing this check made everything work fine. I later (while writing this PR) found out that `__DRI_IMAGE_USE_CURSOR` (to which `GBM_BO_USE_CURSOR` translates) has been deprecated in mesa (https://gitlab.freedesktop.org/mesa/mesa/blob/master/include/GL/internal/dri_interface.h#L1296), which makes me wonder what the usecase of `GBM_BO_USE_CURSOR` is. The reason we never encountered this is that when specifying `GBM_BO_USE_WRITE`, a dumb buffer is created trough DRM and the usage flag never reaches the Intel driver directly. The relevant code is in https://gitlab.freedesktop.org/mesa/mesa/blob/master/src/gbm/backends/dri/gbm_dri.c#L1011-1089 . From this it seems that as long as the size, format and modifiers are right, any surface can be used as a cursor.
2019-02-04 20:47:07 +01:00
(plane && plane->cursor_enabled) ? plane->surf.back : NULL);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y);
if (conn->crtc->gamma_table != NULL) {
size_t size = conn->crtc->gamma_table_size;
uint16_t *r = conn->crtc->gamma_table;
uint16_t *g = conn->crtc->gamma_table + size;
uint16_t *b = conn->crtc->gamma_table + 2 * size;
drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b);
} else {
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
}
}
} else {
2018-07-09 23:49:54 +02:00
wlr_log(WLR_INFO, "DRM fd paused");
2017-05-13 15:12:47 +02:00
}
}
2017-06-03 05:47:33 +02:00
static void drm_invalidated(struct wl_listener *listener, void *data) {
2017-12-07 23:44:59 +01:00
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, drm_invalidated);
2017-06-03 05:47:33 +02:00
2017-09-30 11:22:26 +02:00
char *name = drmGetDeviceNameFromFd2(drm->fd);
2018-07-09 23:49:54 +02:00
wlr_log(WLR_DEBUG, "%s invalidated", name);
2017-06-04 07:43:34 +02:00
free(name);
scan_drm_connectors(drm);
2017-06-02 02:29:10 +02:00
}
backend/drm, backend/libinput: listen to session destroy This fixes a heap-use-after-free when the session is destroyed before the backend during wl_display_destroy: ==1085==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000180 at pc 0x7f88e3590c2d bp 0x7ffdc4e33f90 sp 0x7ffdc4e33f80 READ of size 8 at 0x614000000180 thread T0 #0 0x7f88e3590c2c in find_device ../subprojects/wlroots/backend/session/session.c:192 #1 0x7f88e3590e85 in wlr_session_close_file ../subprojects/wlroots/backend/session/session.c:204 #2 0x7f88e357b80c in libinput_close_restricted ../subprojects/wlroots/backend/libinput/backend.c:24 #3 0x7f88e21af274 (/lib64/libinput.so.10+0x28274) #4 0x7f88e21aff1d (/lib64/libinput.so.10+0x28f1d) #5 0x7f88e219ddac (/lib64/libinput.so.10+0x16dac) #6 0x7f88e21b415d in libinput_unref (/lib64/libinput.so.10+0x2d15d) #7 0x7f88e357c9d6 in backend_destroy ../subprojects/wlroots/backend/libinput/backend.c:130 #8 0x7f88e3545a09 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:50 #9 0x7f88e358981a in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:54 #10 0x7f88e358a059 in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:107 #11 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) #12 0x7f88e314b466 in wl_display_destroy (/lib64/libwayland-server.so.0+0x9466) #13 0x559fefb52385 in main ../main.c:67 #14 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152) #15 0x559fefb4297d in _start (/home/simon/src/glider/build/glider+0x2297d) 0x614000000180 is located 320 bytes inside of 416-byte region [0x614000000040,0x6140000001e0) freed by thread T0 here: #0 0x7f88e3d0a6b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122 #1 0x7f88e35b51fb in logind_session_destroy ../subprojects/wlroots/backend/session/logind.c:270 #2 0x7f88e35905a4 in wlr_session_destroy ../subprojects/wlroots/backend/session/session.c:156 #3 0x7f88e358f440 in handle_display_destroy ../subprojects/wlroots/backend/session/session.c:65 #4 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) previously allocated by thread T0 here: #0 0x7f88e3d0acd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153 #1 0x7f88e35b911c in logind_session_create ../subprojects/wlroots/backend/session/logind.c:746 #2 0x7f88e358f6b4 in wlr_session_create ../subprojects/wlroots/backend/session/session.c:91 #3 0x559fefb51ea6 in main ../main.c:20 #4 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152)
2019-11-30 11:57:37 +01:00
static void handle_session_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, session_destroy);
backend_destroy(&drm->backend);
}
2017-12-07 23:44:59 +01:00
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, display_destroy);
backend_destroy(&drm->backend);
2017-12-07 23:44:59 +01:00
}
2017-05-07 16:00:23 +02:00
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
wlr_renderer_create_func_t create_renderer_func) {
2017-08-05 08:15:39 +02:00
assert(display && session && gpu_fd >= 0);
2017-10-01 08:22:47 +02:00
assert(!parent || wlr_backend_is_drm(parent));
2017-06-03 05:47:33 +02:00
char *name = drmGetDeviceNameFromFd2(gpu_fd);
drmVersion *version = drmGetVersion(gpu_fd);
2018-07-09 23:49:54 +02:00
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
2017-06-03 05:47:33 +02:00
free(name);
drmFreeVersion(version);
2017-09-30 11:22:26 +02:00
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
if (!drm) {
2018-07-09 23:49:54 +02:00
wlr_log_errno(WLR_ERROR, "Allocation failed");
2017-05-07 16:00:23 +02:00
return NULL;
}
2017-09-30 11:22:26 +02:00
wlr_backend_init(&drm->backend, &backend_impl);
2017-09-30 11:22:26 +02:00
drm->session = session;
wl_list_init(&drm->outputs);
2017-05-02 04:34:33 +02:00
2017-09-30 11:22:26 +02:00
drm->fd = gpu_fd;
2018-09-17 22:25:20 +02:00
if (parent != NULL) {
drm->parent = get_drm_backend_from_backend(parent);
}
2017-05-01 07:49:18 +02:00
2017-09-30 11:22:26 +02:00
drm->drm_invalidated.notify = drm_invalidated;
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
2017-06-04 07:43:34 +02:00
2017-09-30 11:22:26 +02:00
drm->display = display;
2017-05-03 11:28:44 +02:00
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
2017-09-30 11:22:26 +02:00
drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
WL_EVENT_READABLE, handle_drm_event, NULL);
2017-09-30 11:22:26 +02:00
if (!drm->drm_event) {
2018-07-09 23:49:54 +02:00
wlr_log(WLR_ERROR, "Failed to create DRM event source");
2017-05-01 07:49:18 +02:00
goto error_fd;
}
2017-09-30 11:22:26 +02:00
drm->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &drm->session_signal);
2017-05-13 15:12:47 +02:00
if (!check_drm_features(drm)) {
2017-08-05 08:15:39 +02:00
goto error_event;
2017-07-20 10:51:59 +02:00
}
if (!init_drm_resources(drm)) {
2017-08-05 08:15:39 +02:00
goto error_event;
}
2017-07-20 10:51:59 +02:00
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
2018-07-09 23:49:54 +02:00
wlr_log(WLR_ERROR, "Failed to initialize renderer");
2017-05-03 11:28:44 +02:00
goto error_event;
}
backend/drm, backend/libinput: listen to session destroy This fixes a heap-use-after-free when the session is destroyed before the backend during wl_display_destroy: ==1085==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000180 at pc 0x7f88e3590c2d bp 0x7ffdc4e33f90 sp 0x7ffdc4e33f80 READ of size 8 at 0x614000000180 thread T0 #0 0x7f88e3590c2c in find_device ../subprojects/wlroots/backend/session/session.c:192 #1 0x7f88e3590e85 in wlr_session_close_file ../subprojects/wlroots/backend/session/session.c:204 #2 0x7f88e357b80c in libinput_close_restricted ../subprojects/wlroots/backend/libinput/backend.c:24 #3 0x7f88e21af274 (/lib64/libinput.so.10+0x28274) #4 0x7f88e21aff1d (/lib64/libinput.so.10+0x28f1d) #5 0x7f88e219ddac (/lib64/libinput.so.10+0x16dac) #6 0x7f88e21b415d in libinput_unref (/lib64/libinput.so.10+0x2d15d) #7 0x7f88e357c9d6 in backend_destroy ../subprojects/wlroots/backend/libinput/backend.c:130 #8 0x7f88e3545a09 in wlr_backend_destroy ../subprojects/wlroots/backend/backend.c:50 #9 0x7f88e358981a in multi_backend_destroy ../subprojects/wlroots/backend/multi/backend.c:54 #10 0x7f88e358a059 in handle_display_destroy ../subprojects/wlroots/backend/multi/backend.c:107 #11 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) #12 0x7f88e314b466 in wl_display_destroy (/lib64/libwayland-server.so.0+0x9466) #13 0x559fefb52385 in main ../main.c:67 #14 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152) #15 0x559fefb4297d in _start (/home/simon/src/glider/build/glider+0x2297d) 0x614000000180 is located 320 bytes inside of 416-byte region [0x614000000040,0x6140000001e0) freed by thread T0 here: #0 0x7f88e3d0a6b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122 #1 0x7f88e35b51fb in logind_session_destroy ../subprojects/wlroots/backend/session/logind.c:270 #2 0x7f88e35905a4 in wlr_session_destroy ../subprojects/wlroots/backend/session/session.c:156 #3 0x7f88e358f440 in handle_display_destroy ../subprojects/wlroots/backend/session/session.c:65 #4 0x7f88e314acde (/lib64/libwayland-server.so.0+0x8cde) previously allocated by thread T0 here: #0 0x7f88e3d0acd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153 #1 0x7f88e35b911c in logind_session_create ../subprojects/wlroots/backend/session/logind.c:746 #2 0x7f88e358f6b4 in wlr_session_create ../subprojects/wlroots/backend/session/session.c:91 #3 0x559fefb51ea6 in main ../main.c:20 #4 0x7f88e2639152 in __libc_start_main (/lib64/libc.so.6+0x27152)
2019-11-30 11:57:37 +01:00
drm->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &drm->session_destroy);
2017-12-07 23:44:59 +01:00
drm->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &drm->display_destroy);
2017-09-30 11:22:26 +02:00
return &drm->backend;
2017-05-01 07:49:18 +02:00
2017-05-03 11:28:44 +02:00
error_event:
wl_list_remove(&drm->session_signal.link);
2017-09-30 11:22:26 +02:00
wl_event_source_remove(drm->drm_event);
2017-05-01 07:49:18 +02:00
error_fd:
2017-09-30 11:22:26 +02:00
wlr_session_close_file(drm->session, drm->fd);
free(drm);
2017-05-01 07:49:18 +02:00
return NULL;
}