โฌข DragonFlyBSD Kernel Audit
DF-4200 / trigger_df4200.c
โ† back to finding โ†“ download raw
/*
 * trigger_df4200.c โ€” DF-4200: NULL encoder_funcs->detect call in
 * amdgpu_connector_dp_detect (sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:1387).
 *
 * On amdgpu hardware: any operation that forces a connector reprobe of a
 * DP-bridge connector (Travis/Nutmeg) whose *first* possible encoder is the
 * external bridge encoder walks into
 *
 *      encoder = amdgpu_connector_best_single_encoder(connector);   (:1335)
 *      ...
 *      encoder_funcs = encoder->helper_private;                    (:1386)
 *      ret = encoder_funcs->detect(encoder, connector);            (:1387)
 *
 * dce_v{8,10,11}_0_ext_helper_funcs have NO .detect ("no detect for
 * TMDS/LVDS yet" โ€” dce_v10_0.c:3411-3418), so on a machine whose BIOS
 * object table enumerates the external bridge before the internal UNIPHY
 * encoder (amdgpu_encoders.c:45-56 attaches in encoder_list order), and
 * where the aux/ddc probe at :1383 fails while dac_load_detect is true
 * (:1385, default-true for DP-bridge VGA/DVI-I connectors โ€”
 * amdgpu_connectors.c:1599,1646), this is a call through a NULL function
 * pointer: instant kernel panic. The sibling path in the same file guards
 * exactly this: `if (encoder_funcs->detect)` (amdgpu_connectors.c:1083).
 *
 * This trigger forces the reprobe via DRM_IOCTL_MODE_GETCONNECTOR with
 * count_modes==0 (probe path) after a hotplug emulation cycle.
 * Self-contained struct defs โ€” no libdrm needed.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>

typedef uint32_t __u32;
typedef uint64_t __u64;

#define DRM_IOCTL_BASE			'd'
#define DRM_IOCTL_MODE_GETRESOURCES	_IOWR(DRM_IOCTL_BASE, 0xA0, struct drm_mode_card_res)
#define DRM_IOCTL_MODE_GETCONNECTOR	_IOWR(DRM_IOCTL_BASE, 0xA7, struct drm_mode_get_connector)

struct drm_mode_modeinfo { __u32 clock, hdisplay, hsync_start, hsync_end, htotal, hskew, vdisplay, vsync_start, vsync_end, vtotal, vscan, vrefresh, flags, type; char name[32]; };
struct drm_mode_card_res {
	__u64 fb_id_ptr, crtc_id_ptr, connector_id_ptr, encoder_id_ptr;
	__u32 count_fbs, count_crtcs, count_connectors, count_encoders;
	__u32 min_width, max_width, min_height, max_height;
};
struct drm_mode_get_connector {
	__u64 encoders_ptr, modes_ptr, props_ptr, prop_values_ptr;
	__u32 count_modes, count_props, count_encoders;
	__u32 encoder_id, connector_id, connector_type, connector_type_id;
	__u32 connection, mm_width, mm_height, subpixel;
};

int main(void)
{
	struct drm_mode_card_res res;
	struct drm_mode_get_connector conn;
	int fd, i;

	fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
	if (fd < 0) {
		printf("DF-4200 trigger: no /dev/dri/card0 โ€” no DRM hardware on "
		       "this guest; amdgpu_connector_dp_detect unreachable.\n");
		return 1;
	}

	memset(&res, 0, sizeof(res));
	if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) {
		perror("GETRESOURCES");
		return 1;
	}
	__u32 ids[32];
	memset(ids, 0, sizeof(ids));
	res.connector_id_ptr = (__u64)ids;
	if (res.count_connectors > 32)
		res.count_connectors = 32;
	res.count_connectors = 0;		/* first: sizes */
	ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
	if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) < 0) {
		perror("GETRESOURCES 2");
		return 1;
	}
	printf("DF-4200 trigger: %u connectors\n", res.count_connectors);

	for (i = 0; i < (int)res.count_connectors; i++) {
		memset(&conn, 0, sizeof(conn));
		conn.connector_id = ids[i];
		conn.count_modes = 0;	/* force the probe path (fill_modes) */
		if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) < 0) {
			perror("GETCONNECTOR");
			continue;
		}
		printf("  connector %u type %u connection %u โ€” probe returned\n",
		       ids[i], conn.connector_type, conn.connection);
	}
	/*
	 * On affected hardware (Travis/Nutmeg DP-bridge laptop, bridge
	 * encoder first in BIOS object table, ddc probe failing,
	 * dac_load_detect=1) the last ioctl above never returns: the kernel
	 * calls NULL encoder_funcs->detect at amdgpu_connectors.c:1387.
	 */
	printf("DF-4200 trigger: completed (no panic on this machine)\n");
	return 0;
}