1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/gpu/drm/vkms/vkms_plane.c
Maíra Canal 6e41acd2e5 drm/vkms: reintroduce prepare_fb and cleanup_fb functions
With commit 359c6649cd ("drm/gem: Implement shadow-plane {begin,
end}_fb_access with vmap"), the behavior of the shadow-plane helpers
changed and the vunmap is now performed at the end of
the current pageflip, instead of the end of the following pageflip.

By performing the vunmap at the end of the current pageflip, invalid
memory is accessed by the vkms during the plane composition, as the data
is being unmapped before being used, as reported by the following
warning:

 [  275.866047] BUG: unable to handle page fault for address: ffffb382814e8002
 [  275.866055] #PF: supervisor read access in kernel mode
 [  275.866058] #PF: error_code(0x0000) - not-present page
 [  275.866061] PGD 1000067 P4D 1000067 PUD 110a067 PMD 46e3067 PTE 0
 [  275.866066] Oops: 0000 [#1] PREEMPT SMP PTI
 [  275.866070] CPU: 2 PID: 49 Comm: kworker/u8:2 Not tainted 6.1.0-rc6-00018-gb357e7ac1b73-dirty #54
 [  275.866074] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.1-2.fc37 04/01/2014
 [  275.866076] Workqueue: vkms_composer vkms_composer_worker [vkms]
 [  275.866084] RIP: 0010:XRGB8888_to_argb_u16+0x5c/0xa0 [vkms]
 [  275.866092] Code: bf 56 0a 0f af 56 70 48 8b 76 28 01 ca 49 83 f8 02
 41 b9 01 00 00 00 4d 0f 43 c8 48 01 f2 48 83 c2 02 31 f6 66 c7 04 f0 ff
 ff <0f> b6 0c b2 89 cf c1 e7 08 09 cf 66 89 7c f0 02 0f b6 4c b2 ff 89
 [  275.866095] RSP: 0018:ffffb382801b7db0 EFLAGS: 00010246
 [  275.866098] RAX: ffff896336ace000 RBX: ffff896310e293c0 RCX: 0000000000000000
 [  275.866101] RDX: ffffb382814e8002 RSI: 0000000000000000 RDI: ffffb382801b7de8
 [  275.866103] RBP: 0000000000001400 R08: 0000000000000280 R09: 0000000000000280
 [  275.866105] R10: 0000000000000010 R11: ffffffffc011d990 R12: ffff896302a1ece0
 [  275.866107] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000080008001
 [  275.866109] FS:  0000000000000000(0000) GS:ffff89637dd00000(0000) knlGS:0000000000000000
 [  275.866112] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
 [  275.866114] CR2: ffffb382814e8002 CR3: 0000000003bb4000 CR4: 00000000000006e0
 [  275.866120] Call Trace:
 [  275.866123]  <TASK>
 [  275.866124]  compose_active_planes+0x1c4/0x380 [vkms]
 [  275.866132]  vkms_composer_worker+0x9f/0x130 [vkms]
 [  275.866139]  process_one_work+0x1c0/0x370
 [  275.866160]  worker_thread+0x221/0x410
 [  275.866164]  ? worker_clr_flags+0x50/0x50
 [  275.866167]  kthread+0xe1/0x100
 [  275.866172]  ? kthread_blkcg+0x30/0x30
 [  275.866176]  ret_from_fork+0x22/0x30
 [  275.866181]  </TASK>
 [  275.866182] Modules linked in: vkms
 [  275.866186] CR2: ffffb382814e8002
 [  275.866191] ---[ end trace 0000000000000000 ]---

Therefore, introduce again prepare_fb and cleanup_fb functions to the
vkms, which were previously removed on commit b43e2ec03b ("drm/vkms:
Let shadow-plane helpers prepare the plane's FB").

Fixes: 359c6649cd ("drm/gem: Implement shadow-plane {begin, end}_fb_access with vmap")
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Maíra Canal <mcanal@igalia.com>
Signed-off-by: Melissa Wen <melissa.srw@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230111131304.106039-1-mcanal@igalia.com
2023-01-12 09:46:19 -01:00

241 lines
6.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
#include <linux/iosys-map.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include "vkms_drv.h"
#include "vkms_formats.h"
static const u32 vkms_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XRGB16161616,
DRM_FORMAT_RGB565
};
static const u32 vkms_plane_formats[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XRGB16161616,
DRM_FORMAT_ARGB16161616,
DRM_FORMAT_RGB565
};
static struct drm_plane_state *
vkms_plane_duplicate_state(struct drm_plane *plane)
{
struct vkms_plane_state *vkms_state;
struct vkms_frame_info *frame_info;
vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
if (!vkms_state)
return NULL;
frame_info = kzalloc(sizeof(*frame_info), GFP_KERNEL);
if (!frame_info) {
DRM_DEBUG_KMS("Couldn't allocate frame_info\n");
kfree(vkms_state);
return NULL;
}
vkms_state->frame_info = frame_info;
__drm_gem_duplicate_shadow_plane_state(plane, &vkms_state->base);
return &vkms_state->base.base;
}
static void vkms_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state);
struct drm_crtc *crtc = vkms_state->base.base.crtc;
if (crtc && vkms_state->frame_info->fb) {
/* dropping the reference we acquired in
* vkms_primary_plane_update()
*/
if (drm_framebuffer_read_refcount(vkms_state->frame_info->fb))
drm_framebuffer_put(vkms_state->frame_info->fb);
}
kfree(vkms_state->frame_info);
vkms_state->frame_info = NULL;
__drm_gem_destroy_shadow_plane_state(&vkms_state->base);
kfree(vkms_state);
}
static void vkms_plane_reset(struct drm_plane *plane)
{
struct vkms_plane_state *vkms_state;
if (plane->state) {
vkms_plane_destroy_state(plane, plane->state);
plane->state = NULL; /* must be set to NULL here */
}
vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
if (!vkms_state) {
DRM_ERROR("Cannot allocate vkms_plane_state\n");
return;
}
__drm_gem_reset_shadow_plane(plane, &vkms_state->base);
}
static const struct drm_plane_funcs vkms_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.reset = vkms_plane_reset,
.atomic_duplicate_state = vkms_plane_duplicate_state,
.atomic_destroy_state = vkms_plane_destroy_state,
};
static void vkms_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct vkms_plane_state *vkms_plane_state;
struct drm_shadow_plane_state *shadow_plane_state;
struct drm_framebuffer *fb = new_state->fb;
struct vkms_frame_info *frame_info;
u32 fmt;
if (!new_state->crtc || !fb)
return;
fmt = fb->format->format;
vkms_plane_state = to_vkms_plane_state(new_state);
shadow_plane_state = &vkms_plane_state->base;
frame_info = vkms_plane_state->frame_info;
memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect));
memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect));
frame_info->fb = fb;
memcpy(&frame_info->map, &shadow_plane_state->data, sizeof(frame_info->map));
drm_framebuffer_get(frame_info->fb);
frame_info->offset = fb->offsets[0];
frame_info->pitch = fb->pitches[0];
frame_info->cpp = fb->format->cpp[0];
vkms_plane_state->plane_read = get_frame_to_line_function(fmt);
}
static int vkms_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct drm_crtc_state *crtc_state;
bool can_position = false;
int ret;
if (!new_plane_state->fb || WARN_ON(!new_plane_state->crtc))
return 0;
crtc_state = drm_atomic_get_crtc_state(state,
new_plane_state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
if (plane->type != DRM_PLANE_TYPE_PRIMARY)
can_position = true;
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
can_position, true);
if (ret != 0)
return ret;
/* for now primary plane must be visible and full screen */
if (!new_plane_state->visible && !can_position)
return -EINVAL;
return 0;
}
static int vkms_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct drm_shadow_plane_state *shadow_plane_state;
struct drm_framebuffer *fb = state->fb;
int ret;
if (!fb)
return 0;
shadow_plane_state = to_drm_shadow_plane_state(state);
ret = drm_gem_plane_helper_prepare_fb(plane, state);
if (ret)
return ret;
return drm_gem_fb_vmap(fb, shadow_plane_state->map, shadow_plane_state->data);
}
static void vkms_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct drm_shadow_plane_state *shadow_plane_state;
struct drm_framebuffer *fb = state->fb;
if (!fb)
return;
shadow_plane_state = to_drm_shadow_plane_state(state);
drm_gem_fb_vunmap(fb, shadow_plane_state->map);
}
static const struct drm_plane_helper_funcs vkms_primary_helper_funcs = {
.atomic_update = vkms_plane_atomic_update,
.atomic_check = vkms_plane_atomic_check,
.prepare_fb = vkms_prepare_fb,
.cleanup_fb = vkms_cleanup_fb,
};
struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
enum drm_plane_type type, int index)
{
struct drm_device *dev = &vkmsdev->drm;
const struct drm_plane_helper_funcs *funcs;
struct vkms_plane *plane;
const u32 *formats;
int nformats;
switch (type) {
case DRM_PLANE_TYPE_PRIMARY:
formats = vkms_formats;
nformats = ARRAY_SIZE(vkms_formats);
funcs = &vkms_primary_helper_funcs;
break;
case DRM_PLANE_TYPE_CURSOR:
case DRM_PLANE_TYPE_OVERLAY:
formats = vkms_plane_formats;
nformats = ARRAY_SIZE(vkms_plane_formats);
funcs = &vkms_primary_helper_funcs;
break;
default:
formats = vkms_formats;
nformats = ARRAY_SIZE(vkms_formats);
funcs = &vkms_primary_helper_funcs;
break;
}
plane = drmm_universal_plane_alloc(dev, struct vkms_plane, base, 1 << index,
&vkms_plane_funcs,
formats, nformats,
NULL, type, NULL);
if (IS_ERR(plane))
return plane;
drm_plane_helper_add(&plane->base, funcs);
return plane;
}