/*
 * DF-2208 / DF-2209 verbatim-code rig - HEAD
 *
 * Compiles the UNMODIFIED production functions
 *   drm_sysfs_connector_add    (sys/dev/drm/drm_sysfs.c:31-46)
 *   drm_sysfs_connector_remove (sys/dev/drm/drm_sysfs.c:48-54)
 * kernel-resident and drives them with fabricated drm_connector state.
 *
 * Everything below the "production-faithful shims" banner mirrors the
 * shipped definitions byte-for-byte in SHAPE (field order, macro
 * semantics, flag values); the ONLY deltas are:
 *   - allocation faults can be injected (rig_fail_kzalloc /
 *     rig_fail_kasprintf) at exactly the call sites the findings cite,
 *     modelling the Linux-GFP_KERNEL OOM window that the finding claims
 *     (on DragonFly GFP_KERNEL == M_WAITOK|M_CACHEALIGN, and M_WAITOK
 *     allocations PANIC or BLOCK instead of returning NULL -- see
 *     sys/kern/kern_slaballoc.c:869-878 limit path, :1722-1724
 *     kernel_map path, :1786-1800 page-starvation retry loop).
 *   - the pool is M_DF22 (own malloc_type) instead of M_DRM so the rig
 *     is self-contained (no drm.ko dependency); zone/slab semantics of
 *     kmalloc/kfree are the real allocator.
 *   - DRM_DEBUG prints unconditionally (content capture).
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/stdarg.h>

MALLOC_DEFINE(M_DF22, "df22pool", "DF2208/DF2209 rig pool (mirrors M_DRM)");

/* ------------------------------------------------------------------ */
/* Production-faithful type shims                                      */
/*   struct kobject ......... include/linux/kobject.h:50-54 (order)    */
/*   struct device .......... include/linux/device.h:44-54 (order)     */
/*   offsetof(struct device, kobj.name) == 8 in production AND here    */
/* ------------------------------------------------------------------ */
struct kref { int refcount; };			/* linux/kref.h */
struct kobj_type {
	void		(*release)(void *);
	const void	*sysfs_ops;
	void		**default_attrs;
};
struct kobject {
	const char	*name;			/* kobject.h:51 */
	struct kref	kref;			/* kobject.h:52 */
	struct kobj_type *ktype;			/* kobject.h:53 */
};
struct dev_pm_info { int rig_pad; };		/* stand-in, past kobj */
struct device {
	struct device	*parent;			/* device.h:45 */
	struct kobject	kobj;				/* device.h:47 */
	void		*bsddev;				/* device.h:49 (device_t) */
	void		*driver_data;			/* device.h:51 */
	struct dev_pm_info power;				/* device.h:53 */
};

struct drm_minor { int index; };
struct drm_device { struct drm_minor *primary; };
struct drm_connector {
	struct device		*kdev;		/* drm_connector.h:833 */
	struct drm_device	*dev;
	char			name[32];	/* drm_connector.h (name[]) */
};

/* linux/gfp.h:16 verbatim */
#define GFP_KERNEL	(M_WAITOK | M_CACHEALIGN)

/* drm_print.h:388 -- normally debug-flag gated; here: capture */
#define DRM_DEBUG(fmt, ...)						\
	kprintf("DF22   [drm_sysfs] " fmt, ##__VA_ARGS__)

/* ------------------------------------------------------------------ */
/* Fault-injecting allocator shims                                     */
/*   kzalloc  == include/linux/slab.h:35  kmalloc(size, M_DRM, f|M_ZERO)*/
/*   kfree    == include/linux/slab.h:38-42 _kfree(ptr, M_DRM) guarded  */
/*   kasprintf== drm_dragonfly.c:87-94 -> kvasprintf drm_dragonfly.c:65 */
/* ------------------------------------------------------------------ */
static int rig_fail_kzalloc;		/* inject NULL at drm_sysfs.c:39 */
static int rig_fail_kasprintf;		/* inject NULL at drm_sysfs.c:40 */
static unsigned long rig_kzalloc_calls, rig_kfree_calls, rig_kasprintf_calls;
static unsigned long rig_alloc_events, rig_free_events;

#undef kzalloc
#define kzalloc(size, flags)	rig_kzalloc((size), (flags))
static void *
rig_kzalloc(size_t size, int flags)
{
	void *p;

	rig_kzalloc_calls++;
	if (rig_fail_kzalloc)
		return (NULL);		/* INJECTION (see file header) */
	p = kmalloc(size, M_DF22, flags | M_ZERO);	/* slab.h:35 shape */
	if (p != NULL)
		rig_alloc_events++;
	return (p);
}

#undef kfree
#define kfree(ptr) do {						\
	if ((ptr) != NULL) {					\
		rig_kfree_calls++;				\
		rig_free_events++;				\
		_kfree((void *)(ptr), M_DF22);			\
	}							\
} while (0)

/* drm_dragonfly.c:65-82 verbatim shape, M_DF22 pool */
static char *
rig_vasprintf(int flags, const char *format, va_list ap)
{
	char *str;
	size_t size;
	va_list aq;

	va_copy(aq, ap);
	size = kvsnprintf(NULL, 0, format, aq);
	va_end(aq);

	str = kmalloc(size + 1, M_DF22, flags);
	if (str == NULL)
		return (NULL);		/* drm_dragonfly.c:78-79 */
	rig_alloc_events++;

	kvsnprintf(str, size + 1, format, ap);
	return (str);
}

#undef kasprintf
#define kasprintf(flags, format, ...)	\
	rig_kasprintf((flags), (format), ##__VA_ARGS__)
static char *
rig_kasprintf(int flags, const char *format, ...)
{
	va_list ap;
	char *str;

	rig_kasprintf_calls++;
	if (rig_fail_kasprintf)
		return (NULL);		/* INJECTION (see file header) */
	va_start(ap, format);
	str = rig_vasprintf(flags, format, ap);
	va_end(ap);
	return (str);
}

/* verbatim (baseline) + fix.diff variants, extracted by build.sh */
int	drm_sysfs_connector_add(struct drm_connector *connector);
void	drm_sysfs_connector_remove(struct drm_connector *connector);
int	drm_sysfs_connector_add_fixed(struct drm_connector *connector);
void	drm_sysfs_connector_remove_fixed(struct drm_connector *connector);
