/*
 * DF-5484 probe -- which of the fixed P-state MSRs does this VMM actually
 * implement?  Exercises the fault-safe accessors ONLY (no raw ops, no
 * panic).  The audited file sys/platform/pc64/acpica/acpi_pstate_machdep.c
 * touches these MSRs with RAW rdmsr()/wrmsr():
 *
 *	Intel: 0x1a0 (MISC_ENABLE,     acpi_pst_intel_init        :506/:509/:511)
 *	       0x199 (PERF_CTL,        acpi_pst_intel_set_pstate  :530/:533)
 *	       0x198 (PERF_STATUS,     acpi_pst_intel_get_pstate  :555)
 *	AMD:   0xc0010062/63/64..      amd1x paths (:227/:259/:280)
 *
 * If rdmsr_safe()/wrmsr_safe() return an error here while CPUID advertises
 * the feature (EST for Intel), the driver's raw accessors would have taken
 * a fatal #GP instead (DF-4735/DF-5155/DF-5160 class).
 */
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>

#define INTEL_MSR_MISC_ENABLE	0x1a0
#define INTEL_MSR_PERF_STATUS	0x198
#define INTEL_MSR_PERF_CTL	0x199
#define AMD1X_MSR_PSTATE_CTL	0xc0010062
#define AMD1X_MSR_PSTATE_ST	0xc0010063
#define AMD1X_MSR_PSTATE_START	0xc0010064

static void
probe_one(const char *name, u_int msr, int wr)
{
	uint64_t val = 0;
	int error;

	error = rdmsr_safe(msr, &val);
	kprintf("msrprobe: %-14s rdmsr_safe(0x%04x) -> error %d, val %#llx\n",
	    name, msr, error, (unsigned long long)val);
	if (error == 0 && wr) {
		error = wrmsr_safe(msr, val);	/* write back same value */
		kprintf("msrprobe: %-14s wrmsr_safe(0x%04x, same) -> error %d\n",
		    name, msr, error);
	}
}

static int
msrprobe_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		kprintf("msrprobe: cpu_vendor_id %d cpu_id %#x "
		    "cpu_feature2 %#x (CPUID2_EST %s)\n",
		    cpu_vendor_id, cpu_id, cpu_feature2,
		    (cpu_feature2 & CPUID2_EST) ? "SET" : "clear");
		probe_one("intel misc_en", INTEL_MSR_MISC_ENABLE, 1);
		probe_one("intel perf_st", INTEL_MSR_PERF_STATUS, 0);
		probe_one("intel perf_ctl", INTEL_MSR_PERF_CTL, 1);
		probe_one("amd pst_ctl", AMD1X_MSR_PSTATE_CTL, 0);
		probe_one("amd pst_st", AMD1X_MSR_PSTATE_ST, 0);
		probe_one("amd pst_0", AMD1X_MSR_PSTATE_START, 0);
		return 0;
	case MOD_UNLOAD:
		return 0;
	}
	return EOPNOTSUPP;
}

static moduledata_t msrprobe_mod = {
	"msrprobe",
	msrprobe_modevent,
	NULL
};
DECLARE_MODULE(msrprobe, msrprobe_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(msrprobe, 1);
