/*
 * DF-5484 trigger -- the EXACT instruction sequence of
 * acpi_pst_intel_set_pstate() (sys/platform/pc64/acpica/acpi_pstate_machdep.c:530-533):
 *
 *	ctl = rdmsr(INTEL_MSR_PERF_CTL);			// :530
 *	ctl &= ~INTEL_MSR_PERF_MASK;				// :531
 *	ctl |= (pstate->st_cval & INTEL_MSR_PERF_MASK);	// :532
 *	wrmsr(INTEL_MSR_PERF_CTL, ctl);			// :533
 *
 * (the ctrl->pr_res == NULL branch, i.e. _PCT Fixed Hardware -- the common
 * Intel BIOS configuration)
 *
 * On this VMM (KVM -cpu host, Skylake) wrmsr_safe(0x199) returns error 14:
 * the MSR write takes #GP.  The raw driver wrmsr therefore takes a fatal
 * kernel #GP (Fatal trap 9) the first time the driver selects a P-state.
 * Reachable (a) unattended at boot via acpi_pst_postattach ->
 * acpi_pst_global_set_pstate, (b) on every
 * `sysctl hw.acpi.cpu.px_global=<freq>` write, (c) on every type-0x80
 * _PPC notify -> acpi_pst_global_fixup_pstate -- on any VMM that
 * advertises CPUID2_EST without virtualizing IA32_PERF_CTL.
 */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/cpufunc.h>

#define INTEL_MSR_PERF_CTL	0x199
#define INTEL_MSR_PERF_MASK	0xffffULL

static int
rawpst_modevent(module_t mod, int type, void *data)
{
	uint64_t ctl, val = 0;
	int error;

	switch (type) {
	case MOD_LOAD:
		kprintf("rawpst: safe probe: rdmsr_safe(0x199) -> ");
		error = rdmsr_safe(INTEL_MSR_PERF_CTL, &val);
		kprintf("error %d val %#llx\n", error,
		    (unsigned long long)val);
		kprintf("rawpst: safe probe: wrmsr_safe(0x199, val) -> ");
		error = wrmsr_safe(INTEL_MSR_PERF_CTL, val);
		kprintf("error %d\n", error);

		kprintf("rawpst: raw driver sequence "
		    "(pstate_machdep.c:530-533), cval=0x0e00 -- "
		    "expect Fatal trap 9\n");
		ctl = rdmsr(INTEL_MSR_PERF_CTL);		/* :530 */
		ctl &= ~INTEL_MSR_PERF_MASK;		/* :531 */
		ctl |= (0x0e00ULL & INTEL_MSR_PERF_MASK);	/* :532 */
		wrmsr(INTEL_MSR_PERF_CTL, ctl);		/* :533 */
		kprintf("rawpst: survived (MSR virtualized)\n");
		return 0;
	case MOD_UNLOAD:
		return 0;
	}
	return EOPNOTSUPP;
}

static moduledata_t rawpst_mod = {
	"rawpst",
	rawpst_modevent,
	NULL
};
DECLARE_MODULE(rawpst, rawpst_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
