"""Fail-closed guard for the current virtio GPU's cursor-free KMS scanout. Only primary planes visible with Sunshine's exact DRM client capabilities are accepted. Virtio cursor hotspot planes are hidden from atomic clients that do not request DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT. This is not a cursor filter. """ import ctypes as C import os import re class PlaneResources(C.Structure): _fields_ = [('count', C.c_uint32), ('planes', C.POINTER(C.c_uint32))] class Plane(C.Structure): _fields_ = [('count_formats', C.c_uint32), ('formats', C.POINTER(C.c_uint32)), ('plane_id', C.c_uint32), ('crtc_id', C.c_uint32), ('fb_id', C.c_uint32), ('crtc_x', C.c_uint32), ('crtc_y', C.c_uint32), ('x', C.c_uint32), ('y', C.c_uint32), ('possible_crtcs', C.c_uint32), ('gamma_size', C.c_uint32)] class Properties(C.Structure): _fields_ = [('count', C.c_uint32), ('props', C.POINTER(C.c_uint32)), ('values', C.POINTER(C.c_uint64))] class PropertyPrefix(C.Structure): _fields_ = [('id', C.c_uint32), ('flags', C.c_uint32), ('name', C.c_char * 32)] class Framebuffer(C.Structure): _fields_ = [('id', C.c_uint32), ('width', C.c_uint32), ('height', C.c_uint32), ('format', C.c_uint32), ('modifier', C.c_uint64), ('flags', C.c_uint32), ('handles', C.c_uint32 * 4), ('pitches', C.c_uint32 * 4), ('offsets', C.c_uint32 * 4)] class VersionPrefix(C.Structure): _fields_ = [('major', C.c_int), ('minor', C.c_int), ('patch', C.c_int), ('name_len', C.c_int), ('name', C.c_char_p)] class VirtioScanout: def __init__(self, config): if set(config) != {'backend', 'card', 'size'} or config['backend'] != 'kms-virtio-no-cursor-plane': raise ValueError('unsupported KMS cursor configuration') self.card = config['card'] if not isinstance(self.card, str) or not re.fullmatch('/dev/dri/card[0-9]+', self.card): raise ValueError('invalid DRM card') self.size = config['size'] if self.size != [2560, 1440]: raise ValueError('unverified KMS geometry') self.drm = C.CDLL('libdrm.so.2') self.drm.drmSetClientCap.argtypes = [C.c_int, C.c_uint64, C.c_uint64] for name, args, result in [ ('drmModeGetPlaneResources', [C.c_int], C.POINTER(PlaneResources)), ('drmModeGetPlane', [C.c_int,C.c_uint32], C.POINTER(Plane)), ('drmModeObjectGetProperties', [C.c_int,C.c_uint32,C.c_uint32], C.POINTER(Properties)), ('drmModeGetProperty', [C.c_int,C.c_uint32], C.POINTER(PropertyPrefix)), ('drmModeGetFB2', [C.c_int,C.c_uint32], C.POINTER(Framebuffer)), ('drmGetVersion', [C.c_int], C.POINTER(VersionPrefix)), ]: fn = getattr(self.drm, name); fn.argtypes = args; fn.restype = result for name in ['drmModeFreePlaneResources','drmModeFreePlane', 'drmModeFreeObjectProperties','drmModeFreeProperty','drmModeFreeFB2', 'drmFreeVersion']: fn = getattr(self.drm, name); fn.argtypes = [C.c_void_p]; fn.restype = None def check(self): fd = os.open(self.card, os.O_RDONLY | os.O_CLOEXEC | os.O_NOFOLLOW) try: version = self.drm.drmGetVersion(fd) if not version: raise RuntimeError('DRM driver unavailable') try: if version.contents.name != b'virtio_gpu': raise RuntimeError('KMS driver changed') finally: self.drm.drmFreeVersion(version) if self.drm.drmSetClientCap(fd, 2, 1): raise RuntimeError('cannot enumerate universal planes') # Match the pinned Sunshine KMS backend. Do not request capability # 6 (cursor-plane hotspots), which would expose the virtio cursor. if self.drm.drmSetClientCap(fd, 3, 1): raise RuntimeError('atomic KMS is required') resources = self.drm.drmModeGetPlaneResources(fd) if not resources: raise RuntimeError('DRM plane list unavailable') try: # This tested virtio device exposes exactly one primary plane. if resources.contents.count != 1: raise RuntimeError('unverified DRM plane topology') plane = self.drm.drmModeGetPlane(fd, resources.contents.planes[0]) if not plane: raise RuntimeError('DRM plane unavailable') try: p = plane.contents props = self.drm.drmModeObjectGetProperties(fd, p.plane_id, 0xeeeeeeee) if not props: raise RuntimeError('DRM plane properties unavailable') kind = None crtc_w = None crtc_h = None try: if props.contents.count > 128: raise RuntimeError('invalid property count') for i in range(props.contents.count): prop = self.drm.drmModeGetProperty(fd, props.contents.props[i]) if not prop: raise RuntimeError('DRM property unavailable') try: name = prop.contents.name value = props.contents.values[i] if name == b'type': kind = value elif name == b'CRTC_W': crtc_w = value elif name == b'CRTC_H': crtc_h = value finally: self.drm.drmModeFreeProperty(prop) finally: self.drm.drmModeFreeObjectProperties(props) # Atomic virtio clients expose CRTC geometry while the # legacy plane fields remain zero. Sunshine uses the same # atomic client capability pair and validates the actual # framebuffer when it captures it. if kind != 1 or [crtc_w, crtc_h] != self.size: raise RuntimeError('scanout is not an active primary-only plane') return {'driver': 'virtio_gpu', 'plane': p.plane_id, 'type': kind, 'crtc': int(p.crtc_id), 'size': self.size, 'cursor_planes': 0} finally: self.drm.drmModeFreePlane(plane) finally: self.drm.drmModeFreePlaneResources(resources) finally: os.close(fd)