#!/bin/sh

#
# File: insmod-sep
#
# Description: script to load SEP driver
#
# Version: 1.11
#
# Copyright (C) 2008 Intel Corporation.  All Rights Reserved.
#
#     This file is part of SEP Development Kit
#
#     SEP Development Kit is free software; you can redistribute it
#     and/or modify it under the terms of the GNU General Public License
#     version 2 as published by the Free Software Foundation.
#
#     SEP Development Kit is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with SEP Development Kit; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
#     As a special exception, you may use this file as part of a free software
#     library without restriction.  Specifically, if other files instantiate
#     templates or use macros or inline functions from this file, or you compile
#     this file and link it with other files to produce an executable, this
#     file does not by itself cause the resulting executable to be covered by
#     the GNU General Public License.  This exception does not however
#     invalidate any other reasons why the executable file might be covered by
#     the GNU General Public License.
#

# ------------------------------ CONSTANTS -----------------------------------

# basic name of driver
DRIVER_NAME=sep5
# name of rmmod script
RMMOD_SCRIPT=rmmod-sep
# default driver device group
DEFAULT_GROUP=${GROUP:-vtune}
# backup group in case group creation fails
BACKUP_GROUP=root
# default driver device permissions
DEFAULT_PERMS=660
# permissions for driver device directory
DIR_PERMS=770

#error codes
SUCCESS=0
INVALID_DRIVER_NAME=1
UNKOWN_ERR=211
ALL_DRIVERS_NOT_FOUND=212
DRIVER_NOT_FOUND=213
INTREE_DRIVER_NOT_FOUND=214
PERMISSION_MISMATCH=215
PAX_LOAD_FAILED=216
SOCPERF_LOAD_FAILED=217
NO_UDEV_MANAGER=225
SOCWATCH_LOAD_FAILED=227
VTSSPP_LOAD_FAILED=228
DEV_CHANGE_PERMS_FAILED=229
DEV_CHANGE_GRP_OWN_FAILED=230
DEV_GROUP_NOT_SPECIFIED=231
DEV_NOT_CREATED=232
DEV_NOT_FOUND=233
DRIVER_LOAD_FAILED=234
MODULE_NOT_FOUND=235
DRIVER_ALREADY_LOADED=236
INVALID_MODULE_FORMAT=237
UNKOWN_SYMBOL_FOUND=238
DRIVER_NOT_SIGNED=239
DRIVER_UNLOAD_FAILED=245
UNABLE_TO_REMOVE_DRIVER=246
UNABLE_TO_REMOVE_ONE_DRIVER=247
UNSUPPORTED_PLATFORM=253
OPTIONS_ERROR=254
COMMANDS_TO_CHECK_FAILED=255
# ------------------------------- OUTPUT -------------------------------------

print_msg()
{
  MSG="$*"
  echo "$MSG"
}

print_nnl()
{
  MSG="$*"
  echo -n "$MSG"
}

print_err()
{
  MSG="$*"
  if [ -w /dev/stderr ]; then
      if [ ! -S /dev/stderr ] ; then
          echo "$MSG" >> /dev/stderr
      else
          echo "$MSG" >&2
      fi
  else
    echo "$MSG"
  fi
}

# set the path to include "standard" locations so commands below can be found
PATH="/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/local/gnu/bin:"${PATH}""
export PATH
# ------------------------------ COMMANDS ------------------------------------

CHGRP="chgrp"
CHMOD="chmod"
CUT="cut"
DIRNAME="dirname"
GREP="grep"
PGREP="pgrep"
INSMOD="insmod"
INSTALL="install"
MKNOD="mknod"
LSMOD="lsmod"
MKDIR="mkdir"
RM="rm"
SED="sed"
STAT="stat"
SU="su"
TR="tr"
UNAME="uname"
WHICH="which"
CAT="cat"
GROUPMOD="groupmod"
YPCAT="ypcat"
GROUPADD="groupadd"
MODINFO="modinfo"
OCTALDUMP="od"
EXPR="expr"

COMMANDS_TO_CHECK="${CUT} ${DIRNAME} ${GREP} ${PGREP} ${INSMOD} ${INSTALL} ${MKNOD} ${LSMOD} ${MKDIR} ${RM} ${SED} ${TR} ${UNAME} ${WHICH} ${CAT} ${OCTALDUMP} ${EXPR}"


# ------------------------------ FUNCTIONS -----------------------------------

max_cpu_list=`${CAT} /sys/devices/system/cpu/present`
max_cpu_temp=${max_cpu_list##*-}
max_cpu=${max_cpu_temp##*,}
num_cpus=$(seq 0 $max_cpu)
num_packages=`${GREP} "physical id" /proc/cpuinfo | tail -1 | ${CUT} -d ":" -f2 | ${CUT} -d " " -f2`
family=`${GREP} -m1 "family"  /proc/cpuinfo  | ${CUT} -d ":" -f2 | ${CUT} -d " " -f2`
model=`${GREP} -m1 "model"    /proc/cpuinfo  | ${CUT} -d ":" -f2 | ${CUT} -d " " -f2`
intree_drivers_loc="/lib/modules/`${UNAME} -r`/kernel/drivers/platform/x86/sepdk/sep/"

# function to show usage and exit
print_usage_and_exit()
{
  err=${1:-0}
  print_msg ""
  print_msg "Usage: $0 [ options ]"
  print_msg ""
  print_msg " where \"options\" are the following:"
  print_msg ""
  print_msg "    -g | --group group"
  print_msg "      restricts access to the ${DRIVER_NAME} driver to users in the specified"
  print_msg "      group; if this option is not provided, the group \"${DEFAULT_GROUP}\""
  print_msg "      will be used"
  print_msg ""
  print_msg "    -p | --perms fileperms"
  print_msg "      restricts access to the ${DRIVER_NAME} driver based on the specified"
  print_msg "      file permissions; if this option is not provided, then file"
  print_msg "      permissions \"${DEFAULT_PERMS}\" (or equivalently, \"ug+rw\") will be used."
  print_msg "      Please provide the permission in octal format, e.g. ${DEFAULT_PERMS}"
  print_msg ""
  print_msg "    -q | --query"
  print_msg "      returns 0 if driver is already loaded, non-zero otherwise;"
  print_msg "      if driver is loaded, information on group ownership"
  print_msg "      and file permissions on driver device will be displayed"
  print_msg ""
  print_msg "    -r | --reload"
  print_msg "      attempt to reload all relevant drivers; note that drivers will"
  print_msg "      not unload if they are still in use"
  print_msg ""
  print_msg "    -pu | --per-user"
  print_msg "      install the sampling driver in secure mode to enable per user collection"
  print_msg ""
  print_msg "    -re | --restricted-environment"
  print_msg "      restricted environment mode: minimal requirements to the system runtime"
  print_msg "      like in busybox case"
  print_msg ""
  print_msg "    --no-udev"
  print_msg "      Create device files seapartely when no device manager is running"
  print_msg ""
  print_msg "    -lid | --load-in-tree-driver"
  print_msg "      install in-tree drivers, if any, available at ${intree_drivers_loc}"
  print_msg ""
  exit $err
}

print_section_header()
{
  #center aligned
  MSG=$1
  LEN=26
  DASH_LINE="---------------------"
  printf "%s%*s" $DASH_LINE $(((${#MSG}+$LEN)/2)) "$MSG"
  printf "%*s%s \n" $(((${#MSG}-$LEN)/2)) " " $DASH_LINE
}

# set the directory of the insmod-sep script
SCRIPT_DIR=`dirname $0`
SEP_SHELL=
SEP_FORCE=-f

VARLOG_DIR="/var/log"


if [ -n "${BUSYBOX_SHELL}" ] ; then
   SEP_SHELL=sh
   SEP_FORCE=
fi

if [ -d "${SCRIPT_DIR}/socperf" ] ; then
    socperfdir="${SCRIPT_DIR}/socperf"
fi

# check for certain options
reload_driver=0
no_udev_mode=0
user_def_perm=0
sep_drivers_only=0
load_in_tree_driver=0
while [ $# -gt 0 ] ; do
  case "$1" in
    -h | --help)
      print_usage_and_exit $SUCCESS
      ;;
    -g | --group)
      DRIVER_GROUP=$2
      if [ -z "$DRIVER_GROUP" ] ; then
        print_err ""
        print_err "ERROR: must provide a group"
        print_usage_and_exit $OPTIONS_ERROR
      fi
      shift
      ;;
    -pu | --per-user)
      SEP_PER_USER=-pu
      ;;
    -p | --perms)
      DRIVER_PERMS=$2
      if [ -z "$DRIVER_PERMS" ] ; then
        print_err ""
        print_err "ERROR: must provide the file permissions"
        print_usage_and_exit $OPTIONS_ERROR
      fi
      user_def_perm=1
      shift
      ;;
    -q | --query)
      err_p=0
      (${SEP_SHELL} ${SCRIPT_DIR}/pax/insmod-pax -q)
      err_p=$?
      (${SEP_SHELL} ${socperfdir}/src/insmod-socperf -q)
      err_socperf=$?
      err_s=0
      err_socwatch=0
      socwatch_exists=false
      driver_loaded=`${LSMOD} | ${GREP} ${DRIVER_NAME} | ${GREP} -v ${DRIVER_NAME}_`
      if [ -z "$driver_loaded" ] ; then
        err_s=${DRIVER_NOT_FOUND}
        print_msg "${DRIVER_NAME} driver is not loaded."
      else
        # below is for non-BUSYBOX case ... need to handle BUSYBOX case too ...
        if [ -e /dev/${DRIVER_NAME}/c ] ; then
          # get group and perms of base controller device
          drv_group_c=`${STAT} -c "%G" /dev/${DRIVER_NAME}/c`
          drv_perms_c=`${STAT} -c "%a" /dev/${DRIVER_NAME}/c`
          # compare against group and perms of module devices
          drv_group=`${STAT} -c "%G" /dev/${DRIVER_NAME}/m`
          if [ "$drv_group" != "$drv_group_c" ] ; then
            err_s=${PERMISSION_MISMATCH}
          fi
          drv_perms=`${STAT} -c "%a" /dev/${DRIVER_NAME}/m`
          if [ "$drv_perms" != "$drv_perms_c" ] ; then
            err_s=${PERMISSION_MISMATCH}
          fi
          # compare against group and perms of the percpu devices
          for minor_no in $num_cpus
          do
            drv_group=`${STAT} -c "%G" /dev/${DRIVER_NAME}/s${minor_no}`
            if [ "$drv_group" != "$drv_group_c" ] ; then
              err_s=${PERMISSION_MISMATCH}
            fi
            drv_perms=`${STAT} -c "%a" /dev/${DRIVER_NAME}/s${minor_no}`
            if [ "$drv_perms" != "$drv_perms_c" ] ; then
              err_s=${PERMISSION_MISMATCH}
            fi
          done
          # check if there were any group or perms mismatches in driver devices
          if [ $err_s -eq ${PERMISSION_MISMATCH} ] ; then
            print_msg "${DRIVER_NAME} driver is loaded but not all subdevices are owned by group \"${drv_group}\" with file permissions \"${drv_perms}\"."
          else
            print_msg "${DRIVER_NAME} driver is loaded and owned by group \"${drv_group}\" with file permissions \"${drv_perms}\"."
          fi
        else
          err_s=${DRIVER_NOT_FOUND}
          print_msg "${DRIVER_NAME} driver is not correctly loaded."
        fi
      fi
      # check if socwatch driver files are present
      if [ -d "${SCRIPT_DIR}/socwatch" ]; then
        # check if driver has been built
        # the driver file and the scripts are present in socwatch/drivers folder
        if [ -d "${SCRIPT_DIR}/socwatch/drivers" ] ; then
          (sh ${SCRIPT_DIR}/socwatch/drivers/insmod-socwatch -q --postfix "${ARCH}-${KERNEL_VERSION}${ARITY}")
          err_socwatch=$?
          socwatch_exists=true
        else
          print_err "Warning: skipping SOCWATCH driver, not built"
        fi
      fi
      if [ -d "${SCRIPT_DIR}/vtsspp" ] ; then
        err_v=0
        (sh ${SCRIPT_DIR}/vtsspp/insmod-vtsspp -q)
        err_v=$?
        if [ $err_v -eq 0 -a $err_p -eq 0 -a $err_s -eq 0 ] ; then
          if [ $socwatch_exists -a $err_socwatch -eq 0 ] || [ !$socwatch_exists ] ; then
            err=0        # all drivers are loaded
          elif [ $socwatch_exists -a $err_socwatch -ne 0 ] ; then
            err=$err_socwatch
          fi
        elif [ $err_p -eq 0 -a $err_s -eq 0 ] ; then
          err=$err_v   # vtsspp not loaded, pax and sep loaded
        elif [ $err_s -eq 0 ] ; then
          err=$err_p   # pax not loaded, sep loaded
        elif [ $err_p -eq 0 ] ; then
          err=$err_s   # pax loaded, sep not loaded
        else
          err=${ALL_DRIVERS_NOT_FOUND}      # neither driver is loaded
        fi
      else
        if [ $err_p -eq 0 -a $err_s -eq 0 ] ; then
          err=0        # both drivers are loaded
        elif [ $err_s -eq 0 ] ; then
          err=$err_p   # pax not loaded, sep loaded
        elif [ $err_p -eq 0 ] ; then
          err=$err_s   # pax loaded, sep not loaded
        else
          err=${ALL_DRIVERS_NOT_FOUND}      # neither driver is loaded
        fi
      fi
      exit $err
      ;;
    -r | --reload)
      reload_driver=1
      ;;
    -sdo | --sep-drivers-only)
      sep_drivers_only=1
      ;;
    -re | --restricted-environment)
      BUSYBOX_SHELL=yes
      ;;
    --no-udev)
      no_udev_mode=1
      ;;
    -lid | --load-in-tree-driver)
      load_in_tree_driver=1
      ;;
    *)
      print_err ""
      print_err "ERROR: unrecognized option \"$1\""
      print_usage_and_exit $OPTIONS_ERROR
      ;;
  esac
  shift
done

#
# Note: Busybox has a restricted shell environment, and
#       conventional system utilities may not be present;
#       so need to account for this ...
#

# busybox binary check
if [ -z "${BUSYBOX_SHELL}" ]; then
  # if not forced by command line option -re then check it
  BUSYBOX_SHELL=` ${GREP} --help 2>&1 | ${GREP} BusyBox`
fi


if [ -n "${BUSYBOX_SHELL}" ] ; then
  DEFAULT_GROUP=${GROUP:-0}
  INSMOD_ADDITIONAL_OPTIONS="$INSMOD_ADDITIONAL_OPTIONS -re"
else
  COMMANDS_TO_CHECK="${CHGRP} ${CHMOD} ${STAT} ${SU} ${COMMANDS_TO_CHECK}"
fi

# if any of the COMMANDS_TO_CHECK are not executable, then exit script
OK="true"
for c in ${COMMANDS_TO_CHECK} ; do
  CMD=`${WHICH} $c 2>&1` ;
  ret_val=$?
  if [ ${ret_val} -ne 0 ] ; then
    OK="false"
    print_err "ERROR: unable to find command \"$c\" !"
  fi
done
if [ ${OK} != "true" ] ; then
  print_err "If you are using BusyBox, please re-run this script with the '-re' flag added"
  print_err "Otherwise, please add the above commands to your PATH and re-run the script ... exiting."
  exit $COMMANDS_TO_CHECK_FAILED
fi

# add exec bit to dir perm based on driver perm
add_exec_bit_to_dir_perm()
{
  driver_access_group=1
  if [ "${user_def_perm}" -eq "0" ] ; then
    return
  fi

  driver_access_group=

  octal_perm=${DRIVER_PERMS}

  # return if invalid permission; it will be caught during permission setting
  if [ "${octal_perm}" -gt "777" ] ; then
    return
  fi

  for shift in 8 4 0 ; do
    # shift to obtain user permission bits at LSB end
    perm=$(( 0x${octal_perm} >> ${shift} ))
    # apply mask to obtain only user permission bits
    perm=$(( ${perm} & 0x7 ))
    # add x bit if a permission bit is set
    if [ "${perm}" -ne "0" ] ; then
      perm=$(( ${perm} | 1 ))
    fi

    if [ "${shift}" -eq "8" ] ; then
      user_perm=${perm}
      if [ "${user_perm}" -ne "0" ] ; then driver_access_user=1 ; fi
    elif [ "${shift}" -eq "4" ] ; then
      grp_perm=${perm}
      if [ "${grp_perm}" -ne "0" ] ; then driver_access_group=1 ; fi
    elif [ "${shift}" -eq "0" ] ; then
      other_perm=${perm}
      if [ "${other_perm}" -ne "0" ] ; then driver_access_other=1 ; fi
    fi
  done

  # concatenate to obtain dir permission
  DIR_PERMS=${user_perm}${grp_perm}${other_perm}
}


# ------------------------------ VARIABLES -----------------------------------

SCRIPT=$0
PLATFORM=`${UNAME} -m`
KERNEL_VERSION=`${UNAME} -r`
PLATFORM=`${UNAME} -m`
DRIVER_DIRECTORY=`${DIRNAME} ${SCRIPT}`
# use existing driver group and device permissions as default if available
if [ -e /dev/${DRIVER_NAME}/c ] && [ -z "${BUSYBOX_SHELL}" ] ; then
  drv_group=`${STAT} -c "%G" /dev/${DRIVER_NAME}`
  drv_perms=`${STAT} -c "%a" /dev/${DRIVER_NAME}/c`
else
  drv_group=${DEFAULT_GROUP}
  drv_perms=${DEFAULT_PERMS}
fi
DRIVER_GROUP=${DRIVER_GROUP:-${drv_group}}
DRIVER_PERMS=${DRIVER_PERMS:-${drv_perms}}


# Obtain the kernel major and minor version
if [ -z "${kernel_version}" ] ; then
  kernel_version=${KERNEL_VERSION}
fi

major=$(echo ${kernel_version} | cut -d. -f1)
minor=$(echo ${kernel_version} | cut -d. -f2)

# update DIR_PERMS based on DRIVER_PERMS
add_exec_bit_to_dir_perm

# create a group if the group does not exist
# check if local group exists
${GROUPMOD} ${DRIVER_GROUP} > /dev/null 2>&1
verifygroup_err=$?
if [ ${verifygroup_err} -ne 0 ] ; then
  # check if nis group exists
  ${YPCAT} group 2> /dev/null | ${CUT} -d : -f1 | ${GREP} -E "^${DRIVER_GROUP}$" > /dev/null 2>&1
  verifygroup_err=$?
  if [ ${verifygroup_err} -ne 0 ] ; then
    getent group ${DRIVER_GROUP} 2> /dev/null | ${GREP} -E "^${DRIVER_GROUP}" > /dev/null 2>&1
    verifygroup_err=$?
    if [ ${verifygroup_err} -ne 0 ]; then
      print_nnl "Creating group ${DRIVER_GROUP} ... "
      ${GROUPADD} ${DRIVER_GROUP} > /dev/null 2>&1
      verifygroup_err=$?
      if [ ${verifygroup_err} -ne 0 ] ; then
        print_err ""
        print_err "Warning: ${DRIVER_GROUP} group creation failed ..."
        DRIVER_GROUP=${BACKUP_GROUP}
        print_err "         proceeding with group ${DRIVER_GROUP} instead ..."
        print_err ""
      else
        print_msg "done"
      fi
    fi
  fi
fi

# check if platform is supported
if [ "${PLATFORM}" = "x86_64" ] ; then
  ARCH="x32_64"
elif [ "${PLATFORM}" = "i386" -o "${PLATFORM}" = "i486" -o "${PLATFORM}" = "i586" -o "${PLATFORM}" = "i686" ] ; then
  ARCH="x32"
else
  print_err ""
  print_err "ERROR: Unsupported platform \"${PLATFORM}\" ... exiting."
  print_err ""
  exit ${UNSUPPORTED_PLATFORM}
fi

# check whether kernel is for UP or SMP
SMP=`${UNAME} -v | ${GREP} SMP`
if [ -z "${SMP}" ] ; then
  ARITY="up"
else
  ARITY="smp"
fi

# check if the device manager exists, and if not, then ask for the option
UDEVD_PID=`${PGREP} udevd`
if [ -z "${UDEVD_PID}" ] ; then
  if [ $no_udev_mode -eq 0 ] ; then
    print_err ""
    print_err "The udev device manager is not running on the system."
    print_err "Recompile the driver with the option '--no-udev'"
    print_err "Then, run this script with the option '--no-udev'"
    print_err ""
    exit ${NO_UDEV_MANAGER}
  fi
fi


# file name of the driver to load
DRIVER_FILENAME=${DRIVER_NAME}${SEP_PER_USER}-${ARCH}-${KERNEL_VERSION}${ARITY}.ko

# ------------------------------- MAIN ---------------------------------------

# check if OS is ClearLinux/IntelNext Based
IS_INTREE_DRIVER_OS=0
IS_CLEAR_LINUX_OS=0
CLEAR_LINUX_OS_RELEASE="/usr/lib/os-release"
if ${GREP} -q "Clear Linux" ${CLEAR_LINUX_OS_RELEASE} ; then
  IS_CLEAR_LINUX_OS=1
  IS_INTREE_DRIVER_OS=1
elif ${ECHO} ${UNAME} -r | ${GREP} -q "intel-next" ; then
  IS_INTREE_DRIVER_OS=1
elif ${ECHO} ${UNAME} -r | ${GREP} -q "svos-next" ; then
  IS_INTREE_DRIVER_OS=1
fi

if [ "${load_in_tree_driver}" -eq 1 ]; then
  if [ "${IS_INTREE_DRIVER_OS}" -eq 1 ]; then
    LINUX_SEP_DRIVER_PATH=`${MODINFO} ${DRIVER_NAME} | ${GREP} filename | ${CUT} -d : -f2 | ${TR} -d "[:blank:]" 2> /dev/null`
    if [ -f "${LINUX_SEP_DRIVER_PATH}" ] ; then
      DRIVER_DIRECTORY=`echo ${LINUX_SEP_DRIVER_PATH} | ${SED} -e "s/\/${DRIVER_NAME}.ko//g"`
      DRIVER_FILENAME=${DRIVER_NAME}.ko
      INSMOD_ADDITIONAL_OPTIONS="$INSMOD_ADDITIONAL_OPTIONS -lid"
    else
      print_err ""
      print_err "Error: ${DRIVER_NAME} is not found in ${intree_drivers_loc}"
      print_err "       Re-run the script without --load-in-tree-driver option to load the drivers available in the installation package"
      print_err ""
      exit ${INTREE_DRIVER_NOT_FOUND}
    fi
  else
    print_err ""
    print_err "WARNING: invalid option --load-in-tree-driver, in-tree driver is not available"
    print_err "         Continuing with ${DRIVER_NAME} driver available in the installation package"
    print_err ""
  fi
fi

# check if driver devices exist, and if so, then exit
DEVNUM=`${GREP} ${DRIVER_NAME} /proc/devices | ${GREP} -v ${DRIVER_NAME}_ | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -n "${DEVNUM}" ] ; then
  if [ $reload_driver -ne 1 ] ; then
    print_err ""
    print_err "The ${DRIVER_NAME} driver is already loaded!  Use ${DRIVER_DIRECTORY}/${RMMOD_SCRIPT} to unload it."
    print_err ""
    exit ${DRIVER_ALREADY_LOADED}
  fi
fi

# check if USER is root
if [ -z "${BUSYBOX_SHELL}" ] ; then
  if [ "${USER}x" != "rootx" ] ; then
    if [ ! -w /dev ] ; then
      print_msg "NOTE:  super-user or \"root\" privileges are required in order to continue."
      print_nnl "Please enter \"root\" "
      OPTIONS="-g ${DRIVER_GROUP} -p ${DRIVER_PERMS} ${SEP_PER_USER}"
      if [ $reload_driver -eq 1 ] ; then
        OPTIONS="$OPTIONS -r"
      fi
      exec ${SU} -c "/bin/sh ${SCRIPT} ${OPTIONS}"
      print_msg ""
      exit ${SUCCESS}
    fi
  fi
fi

if [ -d "${VARLOG_DIR}/${DRIVER_NAME}" ]; then
    ${RM} -rf ${VARLOG_DIR}/${DRIVER_NAME}
fi

${MKDIR} -m u=rwx,g=rx,o= -p ${VARLOG_DIR}/${DRIVER_NAME}
${INSTALL} -m u=rw,g=r,o= /dev/null ${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt

str=$(which python 2>&1)
ret_val=$?
if [ ${ret_val} -ne 0 ]; then
    str=$(which python3 2>&1)
    ret_val=$?
    if [ ${ret_val} -ne 0 ]; then
        print_msg ""
        print_err "Warning: Python is required to gather certain hardware information like DIMM topology"
        print_err "         included as part of \"emon -v\" output, but doesn't affect the tool functionality."
        print_err "         Please install Python and try again."
    elif [ ${str##*/} = "python3" ]; then
        PYTHON=python3
        print_msg "PYTHON is set to $PYTHON"
    fi
elif [ ${str##*/} = "python" ]; then
    PYTHON=python
    print_msg "PYTHON is set to $PYTHON"
fi

# the result of the below scripts are required only on KNL (87) and KNM (133)
# the check for these are being done on the userland binaries
if [ -d "/sys/firmware/dmi/entries/14-0" ]; then
    if [ -f "${SCRIPT_DIR}/read_dmisysfs.py" ]; then
        dmi_config=`${PYTHON} ${SCRIPT_DIR}/read_dmisysfs.py 2> /dev/null`
        str=`echo $dmi_config | ${GREP} "Mode="`
        if [ "KK$str" != "KK" ]; then
            print_msg "$dmi_config" > ${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt
        fi
    fi
fi
if [ -f "/sys/firmware/acpi/tables/SLIT" ]; then
    if [ -f "${SCRIPT_DIR}/read_slitsysfs.py" ]; then
        dmi_config=`${PYTHON} ${SCRIPT_DIR}/read_slitsysfs.py 2> /dev/null`
        str=`echo $dmi_config | ${GREP} "num_proximity_domain="`
        if [ "KK$str" != "KK" ]; then
            print_msg "$dmi_config" >> ${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt
        fi
    fi
fi
if [ -f "/sys/firmware/acpi/tables/SRAT" ]; then
    if [ -f "${SCRIPT_DIR}/read_sratsysfs.py" ]; then
        dmi_config=`${PYTHON} ${SCRIPT_DIR}/read_sratsysfs.py 2> /dev/null`
        str=`echo $dmi_config | ${GREP} "apic_id="`
        if [ "KK$str" != "KK" ]; then
            print_msg "$dmi_config" >> ${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt
        fi
    fi
fi
if [ -c "/dev/mem" ]; then
    if [ -f "${SCRIPT_DIR}/read_smbios.py" ]; then
        dmi_config=`${PYTHON} ${SCRIPT_DIR}/read_smbios.py 2> /dev/null`
        str=`echo $dmi_config | ${GREP} "anchor_str="`
        if [ "KK$str" != "KK" ]; then
            print_msg "$dmi_config" >> ${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt
        fi
    fi
fi

# set minimal permission to the config file
if [ -s "${VARLOG_DIR}/${DRIVER_NAME}/dmi_config.txt" ]; then
    # set driver access group as group
    ${CHGRP} -R ${DRIVER_GROUP} ${VARLOG_DIR}/${DRIVER_NAME} > /dev/null 2>&1
    # remove write permission
    ${CHMOD} -R u-w ${VARLOG_DIR}/${DRIVER_NAME} > /dev/null 2>&1
else
    ${RM} -rf ${VARLOG_DIR}/${DRIVER_NAME}
fi

# check whether to reload the driver
if [ $reload_driver -eq 1 ] ; then
  (${SEP_SHELL} ${SCRIPT_DIR}/${RMMOD_SCRIPT} -s)
  err=$?
  if [ $err -ne 0 ] ; then
    print_err ""
    print_err "ERROR: failed to reload ${DRIVER_NAME} driver"
    print_err ""
    exit $err
  fi
fi


if [ ! -r ${DRIVER_DIRECTORY}/${DRIVER_FILENAME} ] ; then
  print_err ""
  print_err "ERROR: ${DRIVER_NAME} driver \"${DRIVER_FILENAME}\""
  print_err "was not found in directory \"${DRIVER_DIRECTORY}\" !"
  print_err ""
  print_err "This means you may need to build ${DRIVER_NAME} driver from the provided"
  print_err "driver sources. Please see the driver README for instructions."
  print_err ""
  print_err "Note: Ensure that the driver is built for kernel version `uname -r`"
  print_err ""
  exit ${MODULE_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  INSMOD_ADDITIONAL_OPTIONS="$INSMOD_ADDITIONAL_OPTIONS --no-udev"
fi

# check if secure boot is enabled
secure_boot_enabled="Unknown"
print_msg "Detecting Secure Boot status..."
SEC_DIR="/sys/firmware/efi"
# check by mokutil first
result=`command -v mokutil &> /dev/null`
if [ ${result} ]; then
  status=`mokutil --sb-state| grep 'SecureBoot'|cut -f2 -d " "`
  if [ "$status" = "enabled" ]; then
      secure_boot_enabled="True"
  else
      secure_boot_enabled="False"
  fi
else
  # if mokutil is not avaliable, check again by OCTALDUMP as fallback mechanism
  if [ -d $SEC_DIR ]; then
    secure_boot_output=`${OCTALDUMP} --address-radix=n --format=u1 ${SEC_DIR}/efivars/SecureBoot* 2>&1`
    # secure boot is enabled if the 5th bit of above command is 1
    # mokutil --sb-state​, bootctl status can also be used to detect secure boot status
    if [ $? -eq 0 ] ; then
      len=`${EXPR} length "$secure_boot_output"`
      flag_index=5
      secure_boot_flag=`echo $secure_boot_output | ${CUT} -d" " -f${flag_index}`
      if [ $? -eq 0 -a $secure_boot_flag -eq 1 ] ; then
          secure_boot_enabled="True"
      else
          secure_boot_enabled="False"
      fi
    fi
  fi
fi

if [ ${secure_boot_enabled} = "True" ] ; then
  print_msg "Secure Boot is enabled"
elif [ ${secure_boot_enabled} = "False"  ]; then
  print_msg "Secure Boot is disabled"
else
  print_msg "Failed to detect Secure Boot status"
fi

# check Kernel Config - CONFIG_SECURITY_LOCKDOWN_LSM
${GREP} .config -we "CONFIG_SECURITY_LOCKDOWN_LSM=m" > /dev/null 2>&1
config_lockdown_enabled=$?

# check Kernel Config - CONFIG_MODULE_SIG_FORCE
${GREP} .config -we "CONFIG_MODULE_SIG_FORCE=m" > /dev/null 2>&1
config_module_force_enabled=$?

# check if driver is signed
signed="False"
mod_infos=`${MODINFO} ${DRIVER_DIRECTORY}/${DRIVER_FILENAME}`
for info in mod_infos ; do
  if [ ${info} = 'sig_id:' ] ; then
    signed="True"
  fi
done


# try to load the PAX driver if it was not already loaded
print_section_header "Loading PAX driver"
(${SEP_SHELL} ${SCRIPT_DIR}/pax/insmod-pax -g $DRIVER_GROUP -p $DRIVER_PERMS $INSMOD_ADDITIONAL_OPTIONS)
err=$?

if [ $err -eq ${PERMISSION_MISMATCH} ] ; then
  print_err ""
  print_err "Unload the drivers from any installed product version(s) and try again:"
  print_err "         <install_location>/sepdk/src/./rmmod-sep -s"
  print_err ""
  exit $err
elif [ $err -ne ${DRIVER_ALREADY_LOADED} -a $err -ne ${SUCCESS} ] ; then
  print_err ""
  print_err "ERROR: failed to start or connect to required PAX service"
  print_err ""
  exit ${PAX_LOAD_FAILED}
fi


# try to load the SocPerf driver if it was not already loaded
if [ ${IS_CLEAR_LINUX_OS} -eq 0 ] ; then
  if [ -d "${socperfdir}" ] ; then
    print_msg ""
    print_section_header "Loading SOCPERF driver"
    (${SEP_SHELL} ${socperfdir}/src/insmod-socperf -g $DRIVER_GROUP -p $DRIVER_PERMS $INSMOD_ADDITIONAL_OPTIONS)
    err=$?
    if [ $err -eq ${PERMISSION_MISMATCH} ] ; then
      print_err ""
      print_err "Unload the drivers from any installed product version(s) and try again:"
      print_err "         <install_location>/sepdk/src/./rmmod-sep -s"
    elif [ $err -ne ${DRIVER_ALREADY_LOADED} -a $err -ne ${SUCCESS} ] ; then
      print_err ""
      print_err "ERROR: failed to load socperf driver"
      print_err ""
      exit {SOCPERF_LOAD_FAILED}
    fi
  fi
fi


# kallsyms_lookup_name function address
SYMBOL_LOOKUP_FUNC_ADDR=`${GREP} ' T kallsyms_lookup_name' /proc/kallsyms | ${CUT} -d ' ' -f 1`

print_msg ""
print_section_header "Loading SEP driver"

# insmod command to execute
if [ $major -le 4 ] || [ $major -eq 5 -a $minor -le 9 ] ; then
  INSMOD_CMD="${INSMOD} ${DRIVER_DIRECTORY}/${DRIVER_FILENAME}"
else
  INSMOD_CMD="${INSMOD} ${DRIVER_DIRECTORY}/${DRIVER_FILENAME} sym_lookup_func_addr=\"$SYMBOL_LOOKUP_FUNC_ADDR\""
fi

# execute the command
print_msg "Executing: ${INSMOD_CMD}"

INSMOD_OUTPUT=`${INSMOD_CMD} 2>&1`
INSMOD_RESULT=$?

# this lets a system to handle all the device file creation
sleep 1
# abort if unable to load the driver
if [ ${INSMOD_RESULT} -ne 0 ] ; then
  print_err "${INSMOD_OUTPUT}"
  print_err ""
  print_err "ERROR: ${DRIVER_NAME} driver failed to load!"
  print_err ""

  module_output_check=`echo ${INSMOD_OUTPUT} | ${GREP} -i "Invalid module format" 2> /dev/null`
  module_output_result=$?
  if [ ${module_output_result} -eq 0 ]; then
    print_err ""
    print_err "ERROR: Unable to load module"
    dmesg_output=`dmesg | tail -1 2> /dev/null`
    if [ $? -eq 0 ] ; then
      module_dmesg_check=`echo ${dmesg_output} | ${GREP} -i "version magic" 2> /dev/null`
      if [ $? -eq 0 ] ; then
        print_err "dmesg output: ${dmesg_output}"

      fi
    fi
    print_err "Invalid driver module format found. Please rebuild the driver for kernel version `uname -r`."
    print_err ""
    exit ${INVALID_MODULE_FORMAT}
  fi

  symbol_check=`echo ${INSMOD_OUTPUT} | ${GREP} -i "Unknown symbol in module" 2> /dev/null`
  if [ $? -eq 0 ] ; then
    print_err ""
    print_err "ERROR: Unknown symbol found in sep module."
    dmesg_output=`dmesg | tail -1 2> /dev/null`
    print_err "dmesg output: ${dmesg_output}"
    if [ $? -eq 0 ] ; then
      socperf_check=`echo ${dmesg_output} | ${GREP} -i "Unknown symbol SOCPERF_" 2> /dev/null`
      if [ $? -eq 0 ] ; then
        print_err "The socperf driver version loaded on the system is incompatible. Please unload socperf driver and try again."
      fi
    fi
    print_err ""
    exit ${UNKOWN_SYMBOL_FOUND}
  fi

  # check if operation is permitted
  operation_check=`echo ${INSMOD_OUTPUT} | ${GREP} -i "Operation not permitted" 2> /dev/null`
  if [ $? -eq 0 -a $secure_boot_enabled = "True" -a ${config_lockdown_enabled} -ne 0 -a ${signed} = "False" ] ; then
    print_err ""
    print_err "The driver must be signed because the secure boot is enabled and LOCKDOWN_LSM kernel config is set on the system."
    print_err ""
    exit ${DRIVER_NOT_SIGNED}
  fi

  # check if key is rejected
  key_reject_check=`echo ${INSMOD_OUTPUT} | ${GREP} -i "Key was rejected by service" 2> /dev/null`
  if [ $? -eq 0 -a ${config_module_force_enabled} -ne 0 -a ${signed} = "False" ] ; then
    print_err ""
    print_err "The driver must be signed because MODULE_SIG_FORCE kernel config is set on the system."
    print_err ""
    exit ${DRIVER_NOT_SIGNED}
  fi

  sign_check=`echo ${INSMOD_OUTPUT} | ${GREP} -i "required key not available" 2> /dev/null`
  if [ $? -eq 0 -a $secure_boot_enabled = 'True' ] ; then
    print_err ""
    print_err "The driver must be signed because the secure boot is enabled on the system."
    print_err ""
    exit ${DRIVER_NOT_SIGNED}
  fi

  print_err "You may need to build ${DRIVER_NAME} driver for your kernel."
  print_err "Please see the ${DRIVER_NAME} driver README for instructions."
  print_err ""
  exit ${DRIVER_LOAD_FAILED}
fi

# check if the driver has been loaded into the kernel
DEVNUM=`${GREP} ${DRIVER_NAME} /proc/devices | ${GREP} -v ${DRIVER_NAME}_ | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM}" ] ; then
  print_err ""
  print_err "ERROR: unable to find device \"${DRIVER_NAME}\" in /proc/devices !"
  print_err ""
  exit ${DEV_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  # remove base devices that were previously created by the insmod script
  if [ -e /dev/${DRIVER_NAME} ] ; then
    print_nnl "Deleting previously created /dev/${DRIVER_NAME} base devices ... "
    sleep 1
    ${RM} -r ${SEP_FORCE} /dev/${DRIVER_NAME}
    print_msg "done."
  fi

  # create the base devices
  print_nnl "Creating /dev/${DRIVER_NAME} base devices with major number ${DEVNUM} ... "
  sleep 1
  ${MKDIR} -p /dev/${DRIVER_NAME}
  ${MKNOD} /dev/${DRIVER_NAME}/c c ${DEVNUM} 0 && \
  ${MKNOD} /dev/${DRIVER_NAME}/m c ${DEVNUM} 1 && \
  ${MKNOD} /dev/${DRIVER_NAME}/pt c ${DEVNUM} 1 && \
  ${MKNOD} /dev/${DRIVER_NAME}/ptinfo c ${DEVNUM} 1
  MKNOD_RESULT=$?
  if [ ${MKNOD_RESULT} -ne 0 ] ; then
    print_err ""
    print_err "ERROR: unable to create required /dev/${DRIVER_NAME} base devices !"
    print_err ""
    exit ${DEV_NOT_CREATED}
  fi
  print_msg "done."
fi

# check if the driver readers were created
DEVNUM2=`${GREP} -w ${DRIVER_NAME}_s /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM2}" ] ; then
  print_err ""
  print_err "ERROR: unable to find device \"${DRIVER_NAME}_s\" in /proc/devices !"
  print_err ""
  exit ${DEV_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  # create the percpu devices
  print_nnl "Creating /dev/${DRIVER_NAME} percpu devices with major number ${DEVNUM2} ... "
  sleep 1
  for minor_no in $num_cpus
  do
    ${MKNOD} /dev/${DRIVER_NAME}/s${minor_no} c ${DEVNUM2} ${minor_no}
  done
  print_msg "done."
fi

# check if the driver readers were created
DEVNUM3=`${GREP} -w ${DRIVER_NAME}_b /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM3}" ] ; then
  print_err ""
  print_err "ERROR: unable to find device \"${DRIVER_NAME}_b\" in /proc/devices !"
  print_err ""
  exit ${DEV_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  # create the percpu sideband devices
  print_nnl "Creating /dev/${DRIVER_NAME} percpu devices with major number ${DEVNUM3} ... "
  sleep 1
  for minor_no in $num_cpus
  do
    ${MKNOD} /dev/${DRIVER_NAME}/b${minor_no} c ${DEVNUM3} ${minor_no}
  done
  print_msg "done."
fi

# check if the driver readers were created
DEVNUM4=`${GREP} -w ${DRIVER_NAME}_u /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM4}" ] ; then
  print_err ""
  print_err "ERROR: unable to find device \"${DRIVER_NAME}_u\" in /proc/devices !"
  print_err ""
  exit ${DEV_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  # create the per package uncore devices
  print_nnl "Creating /dev/${DRIVER_NAME} per package devices with major number ${DEVNUM4} ... "
  sleep 1
  minor_no=0
  while [ $minor_no -le $num_packages ]
  do
    ${MKNOD} /dev/${DRIVER_NAME}/u${minor_no} c ${DEVNUM4} ${minor_no}
    minor_no=`expr $minor_no + 1`
  done
  print_msg "done."
fi

# check if the driver readers were created
DEVNUM5=`${GREP} -w ${DRIVER_NAME}_e /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM5}" ] ; then
  print_err ""
  print_err "ERROR: unable to find device \"${DRIVER_NAME}_e\" in /proc/devices !"
  print_err ""
  exit ${DEV_NOT_FOUND}
fi

if [ $no_udev_mode -eq 1 ] ; then
  # create the emon controlevices
  print_nnl "Creating /dev/${DRIVER_NAME} device with major number ${DEVNUM4} ... "
  sleep 1
  ${MKNOD} /dev/${DRIVER_NAME}/e${minor_no} c ${DEVNUM5} ${minor_no}
  print_msg "done."
fi

# change group ownership to whichever group is permitted to open the driver
if [ -z "${BUSYBOX_SHELL}" ] ; then
    if [ -z "${DRIVER_GROUP}" ] ; then
      print_err ""
      print_err "ERROR: no group ownership specified for /dev/${DRIVER_NAME} devices ... exiting."
      print_err ""
      exit ${DEV_GROUP_NOT_SPECIFIED}
    fi
    print_nnl "Setting group ownership of devices to group \"${DRIVER_GROUP}\" ... "
    ${CHGRP} -R ${DRIVER_GROUP} /dev/${DRIVER_NAME}
    CHGRP_RESULT=$?
    if [ ${CHGRP_RESULT} -ne 0 ] ; then
      print_err ""
      print_err "ERROR: unable to change group ownership of devices!"
      print_err ""
      exit ${DEV_CHANGE_GRP_OWN_FAILED}
    fi
    print_msg "done."
    print_nnl "Setting file permissions on devices to \"${DRIVER_PERMS}\" ... "
    ${CHMOD} ${DIR_PERMS} /dev/${DRIVER_NAME}
    ${CHMOD} ${DRIVER_PERMS} /dev/${DRIVER_NAME}/*
    CHMOD_RESULT=$?
    if [ ${CHMOD_RESULT} -ne 0 ] ; then
      print_err ""
      print_err "ERROR: unable to change permissions to ${DRIVER_PERMS} on devices!"
      print_err ""
      exit ${DEV_CHANGE_PERMS_FAILED}
    fi
    print_msg "done."
fi

# show which driver was loaded
print_msg "The ${DRIVER_NAME} driver has been successfully loaded."

if [ $sep_drivers_only -eq 0 ] ; then
  # try to load the VTSS++ driver if it was not already loaded
  if [ ${IS_CLEAR_LINUX_OS} -eq 0 ] ; then
    if [ -d "${SCRIPT_DIR}/vtsspp" ] ; then
      print_msg ""
      print_section_header "Loading VTSSPP driver"
      print_nnl "Checking for vtsspp driver ... "
      vtsspp_loaded=`${LSMOD} | ${CUT} -d ' ' -f 1| ${GREP} -E '\<vtsspp\>'`
      if [ -z "$vtsspp_loaded" ] ; then
        print_msg "not detected."
        (${SEP_SHELL} ${SCRIPT_DIR}/vtsspp/insmod-vtsspp -g $DRIVER_GROUP -p $DRIVER_PERMS $INSMOD_ADDITIONAL_OPTIONS)
        err=$?
        if [ $err -ne 0 ] ; then
          exit ${VTSSPP_LOAD_FAILED}
        fi
      else
        print_msg "detected."
      fi
    fi
  fi

  # try to load the socwatch driver if it was not already loaded
  if [ -d "${SCRIPT_DIR}/socwatch" ] && [ -z "${BUSYBOX_SHELL}" ] ; then
    # check if driver has been built
    # the driver file and the scripts are present in socwatch/drivers folder
    print_msg ""
    if [ -d "${SCRIPT_DIR}/socwatch/drivers" ]; then
      print_section_header "Loading SOCWATCH driver"
      print_nnl "Checking for socwatch driver ... "
      socwatch_loaded=`${LSMOD} | ${CUT} -d ' ' -f 1| ${GREP} -E '\<socwatch\>'`
      if [ -z "$socwatch_loaded" ] ; then
        print_msg "not detected."
        (${SEP_SHELL} ${SCRIPT_DIR}/socwatch/drivers/insmod-socwatch -g $DRIVER_GROUP -p $DRIVER_PERMS --postfix "${ARCH}-${KERNEL_VERSION}${ARITY}" )
        err=$?
        if [ $err -ne 0 ] ; then
          exit ${SOCWATCH_LOAD_FAILED}
        fi
      else
        print_msg "detected."
      fi
    else
      print_err "Warning: skipping SOCWATCH driver load, not built"
    fi
  fi
fi

# display driver access group related information for the user
print_msg ""
print_msg "NOTE:"

print_nnl "The driver is accessible "
if [ -n "${driver_access_other}" ] ; then
  print_msg "to all users."
elif [ -n "${driver_access_group}" ] ; then
  print_msg "only to users under the group ${DRIVER_GROUP}."
  print_msg "Please add the users to the group ${DRIVER_GROUP} to use the tool."
elif [ -n "${driver_access_user}" ] ; then
  print_msg "only to the user ${USER}."
fi

print_msg ""
print_msg "To change driver access group, reload the driver using -g <desired_group> option."
print_msg ""

exit ${SUCCESS}
