#!/bin/sh
# - turbo
# - hw_prefetcher
# - check_msr
# - restore_msr
# - msr
# - msrmask
# - msrval

. $LKP_SRC/lib/reproduce-log.sh
. $LKP_SRC/lib/common.sh
. $LKP_SRC/lib/debug.sh

: "${check_msr:=y}"
: "${restore_msr:=y}"

modprobe msr > /dev/null 2>&1

sanity_check()
{
	for cpu_dir in /dev/cpu/[0-9]*
	do
		[ -c $cpu_dir/msr ] || die "$cpu_dir/msr not available"
		return
	done
}

restore_msr()
{
	local reg=$1

	oval=$(rdmsr -c "$reg")
	cat >> "$TMP_RESULT_ROOT/post-run.msr" <<EOF
wrmsr -a "$reg" "$oval"
EOF
}

check_msr()
{
	local reg=$1
	local wrval=$2

	rdval=$(rdmsr -c "$reg")
	# Use arithmetic comparison to eliminate the difference of binary,
	# octal, decimal and hex
	[ $((rdval == wrval)) -ne 0 ] || die "Fail to write $wrval to MSR $reg, get $rdval"
}

# msr tool does not support to set a bit for a register, we have to 1) read
# the value of the register,2) modify the bits at the special postion, 3)
# write the modified value back to the register
setup_msr()
{
	reg=$1
	mask=$2
	val=$3

	rdval=$(rdmsr -c "$reg")
	newval=$(((rdval & ~mask) | (val & mask)))
	parse_bool -q "$restore_msr" && restore_msr "$reg"
	log_cmd wrmsr -a "$reg" "$newval" || exit
	parse_bool -q "$check_msr" && check_msr "$reg" "$newval"
}

setup_turbo()
{
	# According to https://software.intel.com/sites/default/files/managed/
	# 39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf, p4437, Table 2-7
	mask=$((1 << 38))
	if parse_bool -q "$1"; then
		# Enable turbo, set 0 to bit 38
		val=0
	else
		# disable turbo, set 1 to bit 38
		val=$((1 << 38))
	fi
	setup_msr 0x1a0 $mask $val
}

setup_hw_prefetcher()
{
	# According to https://software.intel.com/en-us/articles/disclosure-of-hw-prefetcher-control-on-some-intel-processors
	if parse_bool -q "$1"; then
		setup_msr 0x1a4 0xf 0x0
	else
		setup_msr 0x1a4 0xf 0xf
	fi
}

[ -z "$force_reboot" ] && die "Must set 'force_reboot: 1' to use setup/msr"

sanity_check
# Ignore setup if the environment variable is not defined
[ -n "$turbo" ] && setup_turbo "$turbo"
[ -n "$hw_prefetcher" ] && setup_hw_prefetcher "$hw_prefetcher"
# For debug/test purpose, we also provide to use msr and msrval directly
# to configure CPU msr
[ -n "$msr" -a -n "$msrval" ] && setup_msr "$msr" "$msrmask" "$msrval"
exit 0
