#!/bin/sh # SPDX-License-Identifier: MIT # enne2-cpufreq — helper privilegiato a interfaccia ristretta. # # Installato come /usr/local/libexec/enne2-cpufreq/cpufreq-helper (root:root 0755), # invocato dal plasmoide org.enne2.cpufreq tramite pkexec e una azione polkit # dedicata (org.enne2.cpufreq.set). Non accetta comandi arbitrari: solo i # sottocomandi elencati in usage() e con argomenti validati contro sysfs. # # Uscita: righe key=value; ok=1 in caso di successo, ok=0 + error= + exit 2 # in caso di rifiuto. Ogni scrittura viene riletta per verifica. set -u PATH=/usr/sbin:/usr/bin:/sbin:/bin export PATH umask 022 CMD=${1:-status} [ "$#" -gt 0 ] && shift SELF_DIR=$(dirname "$0") if [ -r "$SELF_DIR/profiles.sh" ]; then # shellcheck source=../../contents/scripts/profiles.sh . "$SELF_DIR/profiles.sh" else printf 'ok=0\nerror=profiles.sh mancante accanto all helper\n' exit 2 fi CPUFREQ=/sys/devices/system/cpu/cpufreq INTEL_PSTATE=/sys/devices/system/cpu/intel_pstate STATE_DIR=/var/lib/enne2-cpufreq BASELINE=$STATE_DIR/baseline REFCOUNT=$STATE_DIR/refcount usage() { cat <<'EOF' uso: cpufreq-helper [argomenti] status stato corrente (leggibile anche senza root) set-governor powersave | performance | ... set-epp default | performance | balance_performance | balance_power | power set-limits set-boost <0|1> apply quiet | powersave | balanced | performance reset ripristina i default hardware (boost on, nessun tetto) save-state salva lo stato corrente come baseline restore-state ripristina la baseline salvata task-begin salva la baseline (se serve) e applica il profilo task-end [--all] decrementa il contatore e ripristina la baseline a zero EOF } fail() { printf 'ok=0\nerror=%s\n' "$*" exit 2 } readf() { # readf -> valore o exit 1 [ -r "$1" ] || return 1 _val= IFS= read -r _val < "$1" 2>/dev/null || [ -n "$_val" ] || return 1 printf '%s\n' "$_val" } policies() { for _p in "$CPUFREQ"/policy[0-9]*; do [ -d "$_p" ] && printf '%s\n' "$_p" done } write_one() { # write_one [ -w "$1" ] || return 1 printf '%s\n' "$2" > "$1" 2>/dev/null } # Scrive un attributo su TUTTE le policy. Imposta WROTE e FAILED. write_all() { _attr=$1 _val=$2 WROTE=0 FAILED=0 for _p in $(policies); do if write_one "$_p/$_attr" "$_val"; then WROTE=$((WROTE + 1)) else FAILED=$((FAILED + 1)) fi done [ "$FAILED" -eq 0 ] && [ "$WROTE" -gt 0 ] } # Valori distinti letti per un attributo, spazio-separati. attr_values() { for _p in $(policies); do readf "$_p/$1" done | sort -u | tr '\n' ' ' | sed 's/ *$//' } p0=$(policies | head -n 1) [ -n "$p0" ] || fail "nessuna policy cpufreq in $CPUFREQ" driver=$(readf "$p0/scaling_driver" || echo unknown) hw_min=$(readf "$p0/cpuinfo_min_freq" || echo 0) hw_max=$(cf_hw_max_khz "$p0") # indipendente dallo stato del boost floor=$(readf "$p0/amd_pstate_lowest_nonlinear_freq" || echo "") [ -n "$floor" ] && [ "$floor" -gt "$hw_min" ] || floor=$hw_min boost_global=$CPUFREQ/boost [ -w "$boost_global" ] || [ -r "$boost_global" ] || boost_global="" boost_read() { for _p in $(policies); do readf "$_p/boost" && return 0 done [ -n "$boost_global" ] && readf "$boost_global" && return 0 readf "$INTEL_PSTATE/no_turbo" >/dev/null 2>&1 || return 1 _nt=$(readf "$INTEL_PSTATE/no_turbo") || return 1 [ "$_nt" = "1" ] && echo 0 || echo 1 } print_state() { printf 'driver=%s\n' "$driver" printf 'mode=%s\n' "$(readf /sys/devices/system/cpu/amd_pstate/status || echo n/a)" printf 'governor=%s\n' "$(attr_values scaling_governor)" printf 'epp=%s\n' "$(attr_values energy_performance_preference)" printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")" printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")" printf 'hw_min_khz=%s\n' "$hw_min" printf 'hw_max_khz=%s\n' "$hw_max" printf 'floor_khz=%s\n' "$floor" printf 'boost=%s\n' "$(boost_read || echo n/a)" printf 'policies=%s\n' "$(policies | wc -l | tr -d ' ')" printf 'baseline=%s\n' "$([ -r "$BASELINE" ] && echo present || echo absent)" printf 'refcount=%s\n' "$([ -r "$REFCOUNT" ] && readf "$REFCOUNT" || echo 0)" } set_governor() { _g=${1:-} [ -n "$_g" ] || fail "governor mancante" case "$_g" in *[!a-z_-]*) fail "governor non valido: $_g" ;; esac _avail=$(readf "$p0/scaling_available_governors") || fail "governor non leggibili" case " $_avail " in *" $_g "*) ;; *) fail "governor '$_g' non disponibile ($_avail)" ;; esac write_all scaling_governor "$_g" || fail "scrittura scaling_governor fallita su $FAILED policy" _now=$(attr_values scaling_governor) [ "$_now" = "$_g" ] || fail "verifica governor fallita: letto '$_now'" printf 'governor=%s\n' "$_g" } set_epp() { _e=${1:-} [ -n "$_e" ] || fail "preferenza EPP mancante" case "$_e" in *[!a-z_]*) fail "preferenza EPP non valida: $_e" ;; esac _avail=$(readf "$p0/energy_performance_available_preferences") \ || fail "questo driver non espone energy_performance_preference" case " $_avail " in *" $_e "*) ;; *) fail "EPP '$_e' non disponibile ($_avail)" ;; esac if write_all energy_performance_preference "$_e"; then printf 'epp=%s\n' "$_e" return 0 fi # Con governor "performance" il driver EPP può ignorare la preferenza: non è # un errore fatale, viene segnalato come warning. if [ "$(attr_values scaling_governor)" = "performance" ]; then printf 'warning=EPP non applicato con governor performance\n' return 0 fi fail "scrittura EPP fallita su $FAILED policy" } set_boost() { _b=${1:-} case "$_b" in 0|1) ;; *) fail "boost deve essere 0 o 1" ;; esac _count=0 for _p in $(policies); do if [ -w "$_p/boost" ] && write_one "$_p/boost" "$_b"; then _count=$((_count + 1)) fi done if [ -n "$boost_global" ] && write_one "$boost_global" "$_b"; then _count=$((_count + 1)) fi if [ -w "$INTEL_PSTATE/no_turbo" ] && write_one "$INTEL_PSTATE/no_turbo" $((1 - _b)); then _count=$((_count + 1)) fi [ "$_count" -gt 0 ] || fail "nessun controllo boost scrivibile su questo sistema" _now=$(boost_read || echo "") [ "$_now" = "$_b" ] || printf 'warning=lettura boost dopo la scrittura: %s\n' "${_now:-n/d}" printf 'boost=%s\n' "$_b" } set_limits() { _min=${1:-} _max=${2:-} case "$_min$_max" in *[!0-9]*) fail "limiti non numerici (kHz interi)" ;; esac [ -n "$_min" ] && [ -n "$_max" ] || fail "servono min_khz e max_khz" [ "$_min" -ge "$hw_min" ] || fail "minimo $_min sotto il limite hardware $hw_min" [ "$_max" -le "$hw_max" ] || fail "massimo $_max sopra il limite hardware $hw_max" [ "$_min" -le "$_max" ] || fail "minimo $_min maggiore del massimo $_max" _cur_min=$(readf "$p0/scaling_min_freq" || echo "$hw_min") _cur_max=$(readf "$p0/scaling_max_freq" || echo "$hw_max") # Ordine di scrittura sicuro: mai uno stato intermedio con min > max. if [ "$_min" -gt "$_cur_max" ]; then write_all scaling_max_freq "$_max" || fail "pre-allargamento del tetto fallito" fi if [ "$_max" -lt "$_cur_min" ]; then write_all scaling_min_freq "$_min" || fail "pre-abbassamento del minimo fallito" fi write_all scaling_min_freq "$_min" || fail "scrittura scaling_min_freq fallita su $FAILED policy" write_all scaling_max_freq "$_max" || fail "scrittura scaling_max_freq fallita su $FAILED policy" printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")" printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")" } apply_profile() { _pr=${1:-} cf_profile_known "$_pr" || fail "profilo '$_pr' sconosciuto ($(cf_profiles | tr '\n' ' '))" _gov=$(cf_profile_gov "$_pr") _epp=$(cf_profile_epp "$_pr") _bst=$(cf_profile_boost "$_pr") _maxf=$(cf_profile_max_khz "$_pr" "$floor" "$hw_max") [ "$_maxf" -gt "$hw_max" ] && _maxf=$hw_max [ "$_maxf" -lt "$floor" ] && _maxf=$floor printf 'profile=%s\n' "$_pr" # Ordine: 1) boost, 2) governor, 3) limiti, 4) EPP. # Il boost viene per primo perché con boost disattivato il kernel riduce il # tetto nominale e una scrittura di scaling_max_freq oltre quel valore # verrebbe rifiutata o limitata. set_boost "$_bst" || return 1 if readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then set_governor "$_gov" || return 1 else printf 'warning=governor %s non disponibile, mantenuto %s\n' \ "$_gov" "$(attr_values scaling_governor)" fi set_limits "$floor" "$_maxf" || return 1 if readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then set_epp "$_epp" || return 1 fi printf 'profile_label=%s\n' "$(cf_profile_label "$_pr")" } ensure_state_dir() { if [ ! -d "$STATE_DIR" ]; then mkdir -p "$STATE_DIR" || fail "impossibile creare $STATE_DIR" chmod 755 "$STATE_DIR" fi } save_state() { ensure_state_dir { printf 'governor=%s\n' "$(readf "$p0/scaling_governor")" printf 'epp=%s\n' "$(readf "$p0/energy_performance_preference" || echo "")" printf 'min_khz=%s\n' "$(readf "$p0/scaling_min_freq")" printf 'max_khz=%s\n' "$(readf "$p0/scaling_max_freq")" printf 'boost=%s\n' "$(boost_read || echo "")" printf 'saved_at=%s\n' "$(date +%s)" } > "$BASELINE" || fail "scrittura baseline fallita" chmod 644 "$BASELINE" printf 'baseline=saved\n' cat "$BASELINE" } restore_state() { [ -r "$BASELINE" ] || fail "nessuna baseline salvata" _gov=$(sed -n 's/^governor=//p' "$BASELINE") _epp=$(sed -n 's/^epp=//p' "$BASELINE") _min=$(sed -n 's/^min_khz=//p' "$BASELINE") _max=$(sed -n 's/^max_khz=//p' "$BASELINE") _bst=$(sed -n 's/^boost=//p' "$BASELINE") if [ -n "$_gov" ]; then if readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then write_all scaling_governor "$_gov" || printf 'warning=governor non ripristinato\n' fi fi if [ -n "$_min" ] && [ -n "$_max" ]; then case "$_min$_max" in *[!0-9]*) ;; *) set_limits "$_min" "$_max" || printf 'warning=limiti non ripristinati\n' ;; esac fi if [ -n "$_epp" ] && readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then write_all energy_performance_preference "$_epp" || printf 'warning=EPP non ripristinato\n' fi if [ -n "$_bst" ]; then case "$_bst" in 0|1) set_boost "$_bst" >/dev/null || printf 'warning=boost non ripristinato\n' ;; esac fi printf 'restored=1\n' print_state } reset_cmd() { _gov=powersave if ! readf "$p0/scaling_available_governors" | grep -qw -- "$_gov"; then _gov=$(readf "$p0/scaling_available_governors" | awk '{print $1}') fi printf 'profile=reset\n' [ -n "$_gov" ] && set_governor "$_gov" set_limits "$floor" "$hw_max" || return 1 if readf "$p0/energy_performance_available_preferences" >/dev/null 2>&1; then _epp=balance_performance if ! readf "$p0/energy_performance_available_preferences" | grep -qw -- "$_epp"; then _epp=default fi set_epp "$_epp" || return 1 fi set_boost 1 || return 1 } task_begin() { _pr=${1:-} cf_profile_known "$_pr" || fail "profilo '$_pr' sconosciuto" ensure_state_dir _rc=0 [ -r "$REFCOUNT" ] && _rc=$(readf "$REFCOUNT") case "${_rc:-0}" in *[!0-9]*|"") _rc=0 ;; esac if [ "$_rc" -eq 0 ] || [ ! -r "$BASELINE" ]; then save_state >/dev/null || fail "salvataggio dello stato corrente fallito" fi apply_profile "$_pr" || fail "applicazione del profilo '$_pr' fallita" _rc=$((_rc + 1)) printf '%s\n' "$_rc" > "$REFCOUNT" chmod 644 "$REFCOUNT" printf 'refcount=%s\n' "$_rc" } task_end() { ensure_state_dir _rc=0 [ -r "$REFCOUNT" ] && _rc=$(readf "$REFCOUNT") case "${_rc:-0}" in *[!0-9]*|"") _rc=0 ;; esac if [ "${1:-}" = "--all" ]; then _rc=0 else _rc=$((_rc - 1)) [ "$_rc" -lt 0 ] && _rc=0 fi printf '%s\n' "$_rc" > "$REFCOUNT" chmod 644 "$REFCOUNT" if [ "$_rc" -eq 0 ] && [ -r "$BASELINE" ]; then restore_state >/dev/null || printf 'warning=ripristino della baseline fallito\n' rm -f "$BASELINE" printf 'restored=1\n' fi printf 'refcount=%s\n' "$_rc" } case "$CMD" in status) print_state ;; usage|-h|--help|help) usage ;; set-governor) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" set_governor "${1:-}" ;; set-epp) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" set_epp "${1:-}" ;; set-limits) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" set_limits "${1:-}" "${2:-}" ;; set-boost) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" set_boost "${1:-}" ;; apply) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" apply_profile "${1:-}" ;; reset) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" reset_cmd ;; save-state) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" save_state ;; restore-state) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" restore_state ;; task-begin) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" task_begin "${1:-}" ;; task-end) [ "$(id -u)" -eq 0 ] || fail "operazione privilegiata: usare pkexec" task_end "${1:-}" ;; *) printf 'ok=0\nerror=comando sconosciuto: %s\n' "$CMD" usage exit 2 ;; esac printf 'ok=1\n' exit 0