Przeglądaj źródła

飞腾D3000m reboot/S3/S4 function

Joe 1 miesiąc temu
commit
78f0a587e7

+ 35 - 0
CMakeLists.txt

@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.16)
+project(RunTest VERSION 1.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
+
+# 尝试寻找 Qt6,如果找不到则降级寻找 Qt5
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Gui Widgets)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets)
+
+# 包含所有的源文件和头文件
+file(GLOB_RECURSE SRC_FILES
+    "src/*.cpp"
+    "src/*.h"
+)
+
+# 可执行文件
+add_executable(RunTest main.cpp ${SRC_FILES})
+target_link_libraries(RunTest PRIVATE
+    Qt${QT_VERSION_MAJOR}::Core
+    Qt${QT_VERSION_MAJOR}::Gui
+    Qt${QT_VERSION_MAJOR}::Widgets
+)
+
+# 添加 include 目录
+target_include_directories(RunTest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+# MSVC 编译器下强制使用 UTF-8 编码
+if(MSVC)
+    target_compile_options(RunTest PRIVATE /utf-8)
+endif()
+ 

+ 14 - 0
boot_test/EcIO/Makefile

@@ -0,0 +1,14 @@
+ARCH := $(shell arch)
+
+ifeq ($(ARCH), aarch64)
+	CFLAGS += -DFT -g
+endif
+
+all:lib
+	gcc *.c -I . -o ec$(ARCH) $(CFLAGS)
+
+clean:
+	rm *.o ec$(ARCH) libec$(ARCH).so
+
+lib:
+	gcc eclib.c -fPIC -shared -o libec$(ARCH).so $(CFLAGS)

+ 181 - 0
boot_test/EcIO/eclib.c

@@ -0,0 +1,181 @@
+#include "eclib.h"
+#include <sys/mman.h>
+
+static int fd;
+
+#ifdef FT
+
+#define CONTROL_NOTE "/dev/mem"
+const off_t OFF_EC_PORT = 0x20000000;
+uint64_t map_addr = MAP_FAILED;
+
+bool port_dev_init(void)
+{
+    fd = open(CONTROL_NOTE, O_RDWR | O_SYNC);
+    if (fd < 0) {
+        return false;
+    }
+    map_addr = (uint64_t)mmap(NULL, 0x1000, PROT_WRITE|O_SYNC, MAP_SHARED, fd, OFF_EC_PORT);
+    if (map_addr == MAP_FAILED) {
+        return false;
+    }
+    return true;
+}
+
+void port_dev_exit(void)
+{
+    if (map_addr != MAP_FAILED) {
+        munmap((void*)map_addr, 0x1000);
+    }
+    if (fd > 0)
+        close(fd);
+}
+
+uint8_t ioread8(uint16_t port)
+{
+    uint8_t val = 0;
+    val = *(uint8_t *)(map_addr + port);
+    return val;
+}
+
+void iowrite8(uint16_t port, uint8_t data)
+{
+    *(uint8_t *)(map_addr + port) = data;
+}
+
+#else
+
+#define CONTROL_NOTE "/dev/port"
+
+bool port_dev_init(void)
+{
+    fd = open(CONTROL_NOTE, O_SYNC);
+    if (fd < 0) {
+        return false;
+    }  
+    return true;
+}
+
+void port_dev_exit(void)
+{
+    close(fd);
+}
+
+uint8_t ioread8(uint16_t port)
+{
+    uint8_t buf[2] = { 0 };
+
+    lseek(fd, port, SEEK_SET);
+    read(fd, buf, 1);
+    return buf[0];
+}
+
+void iowrite8(uint16_t port, uint8_t data)
+{
+    uint8_t buf[2] = { 0 };
+    printf("OFF_EC_D_PORT = 0x%X \n", port);
+    lseek(fd, port, SEEK_SET);
+    buf[0] = data;
+    write(fd, buf, 1);
+}
+
+#endif
+
+bool wait_kbc_ibe(uint16_t cmd_state)
+{
+    uint8_t  kbd_cmd_state = 0;
+    uint64_t index;
+
+    for (index = 0; index < EC_TIME_OUT; index ++) {
+        kbd_cmd_state = (uint8_t)ioread8(cmd_state);
+        if (!(kbd_cmd_state & EC_S_IBF)) {
+            return true;
+        }
+        else {
+            //delay
+        }
+    }
+    printf("wait ibe time out\n");
+    return false;
+}
+
+bool wait_kbc_obf (uint16_t cmd_state)
+{
+    uint8_t  kbd_cmd_state = 0;
+    uint64_t index;
+
+    for (index = 0; index < EC_TIME_OUT; index ++) {
+        kbd_cmd_state = (uint8_t)ioread8(cmd_state);
+        if (kbd_cmd_state & EC_S_OBF) {
+            return true;
+        }
+        else {
+            //delay
+        }
+    }
+    printf("wait ibe time out\n");
+    return false;
+}
+
+void send_data (uint8_t port, uint8_t data)
+{
+    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
+    iowrite8 (port - 4, data);
+    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
+}
+
+
+uint8_t get_data (uint8_t port, bool kb_ms_ctrl)
+{
+    uint8_t data;
+
+    wait_kbc_ibe (port);                   // Wait Input Buffer Empty
+    wait_kbc_obf (port);                   // Wait Output Buffer Full
+    data = ioread8 (port - 4);
+
+    return data;
+}
+
+void send_cmd(uint8_t port, uint8_t cmd, bool kb_ms_ctrl)
+{
+    wait_kbc_ibe(port);
+    iowrite8(port, cmd);
+    wait_kbc_ibe(port);
+}
+
+uint8_t read_ec_ram(uint8_t index)
+{
+    send_cmd (EC_C_PORT, EC_C_READ_MEM, false);
+    /* send ECRAM offset to DATA port */
+    send_data (EC_C_PORT, index);
+    /* get DATA from EC */
+    return get_data (EC_C_PORT, false);
+}
+
+void write_ec_ram(uint8_t index, uint8_t value)
+{
+    /* Write Command */
+    send_cmd (EC_C_PORT, EC_C_WRITE_MEM, false);
+    /* send ECRAM offset to DATA port */
+    send_data (EC_C_PORT, index);
+    /* Write DATA to EC ram */
+    send_data (EC_C_PORT, value);
+}
+
+uint8_t read_ec(uint16_t offset)
+{
+    uint8_t data;
+
+    iowrite8 ((uint16_t)(EC_IO_BASE + 1), (uint8_t)((offset & 0xFF00) >> 8));
+    iowrite8 ((uint16_t)(EC_IO_BASE + 2), (uint8_t)(offset & 0x00FF));
+    data =  ioread8 ((uint16_t)(EC_IO_BASE + 3));
+
+    return data;
+}
+
+void write_ec(uint16_t offset, uint8_t data)
+{
+    iowrite8 ((uint16_t)(EC_IO_BASE + 1), (uint8_t)((offset & 0xFF00) >> 8));
+    iowrite8 ((uint16_t)(EC_IO_BASE + 2), (uint8_t)(offset & 0x00FF));
+    iowrite8 ((uint16_t)(EC_IO_BASE + 3), data);
+}

+ 60 - 0
boot_test/EcIO/eclib.h

@@ -0,0 +1,60 @@
+#ifndef _EC_LIB_H_
+#define _EC_LIB_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define EC_TIME_OUT          0x1000
+
+//
+// The EC implements an embedded controller interface at ports 0x60/0x64 and a ACPI compliant
+// system management controller at ports 0x62/0x66. Port 0x66 is the command and status port,
+// port 0x62 is the data port.
+//
+#define EC_D_PORT        0x62
+#define EC_C_PORT        0x66
+
+//
+// Status Port 0x62
+//
+#define EC_S_OVR_TMP     0x80    // Current CPU temperature exceeds the threshold
+#define EC_S_SMI_EVT     0x40    // SMI event is pending
+#define EC_S_SCI_EVT     0x20    // SCI event is pending
+#define EC_S_BURST       0x10    // EC is in burst mode or normal mode
+#define EC_S_CMD         0x08    // Byte in data register is command/data
+#define EC_S_IGN         0x04    // Ignored
+#define EC_S_IBF         0x02    // Input buffer is full/empty
+#define EC_S_OBF         0x01    // Output buffer is full/empty
+
+//
+// EC commands that are issued to the EC through the command port (0x66).
+// New commands and command parameters should only be written by the host when IBF=0.
+// Data read from the EC data port is valid only when OBF=1.
+//
+#define EC_C_READ_MEM          0x80
+#define EC_C_WRITE_MEM         0x81
+#define EC_C_GET_QEVENT        0x84
+#define EC_ACPI_MODE_EN_CMD    0x86
+#define EC_ACPI_MODE_DIS_CMD   0x87
+
+//
+// EC IO base address
+//
+#define EC_IO_BASE             0x390
+
+//
+// Declare some functions to access EC
+//
+bool port_dev_init(void);
+void port_dev_exit(void);
+
+uint8_t read_ec_ram(uint8_t index);
+void write_ec_ram(uint8_t index, uint8_t value);
+
+uint8_t read_ec(uint16_t offset);
+void write_ec(uint16_t offset, uint8_t data);
+
+#endif

BIN
boot_test/EcIO/ecmips64


BIN
boot_test/EcIO/ecx86_64


BIN
boot_test/EcIO/libecmips64.so


BIN
boot_test/EcIO/libecx86_64.so


+ 67 - 0
boot_test/EcIO/main.c

@@ -0,0 +1,67 @@
+#include "eclib.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int print_all()
+{
+    uint8_t value = 0;
+    for (int i = 0; i <= 0x0F; i ++) {
+        for (int j = 0; j <= 0x0F; j ++) {
+            value = read_ec_ram(i * 16 + j);
+            printf("%02X ", value);
+        }
+        printf("\n");
+    }
+}
+
+int main(int argc, char **argv)
+{
+    uint8_t value = 0;
+    uint8_t addr = 0;
+    bool isread = true;
+    int ch;
+     if (!port_dev_init()) {
+        printf("Please run as root user!\n");
+        return -1;
+    }
+    if (argc > 1)
+    {
+        while ((ch = getopt(argc, argv, "a:rw:l")) != -1)
+        {
+            switch (ch) 
+            {
+                case 'a':
+                    addr = strtol(optarg, NULL, 16);
+                    break;
+                case 'r':
+                    isread = true;
+                    break;
+                case 'w':
+                    isread = false;
+                    value = strtol(optarg, NULL, 16);
+                    break;
+                case '?':
+                    printf("-a [address] -r read -w [value] write\n");
+                    return -1;
+            }
+        }
+        if(isread)
+        {
+            printf("0x%02X", read_ec_ram(addr)); 
+        }
+        else
+        {
+            printf("write 0x%02X=0x%02X\n", addr, value);
+            write_ec_ram(addr, value);
+        }
+    }
+    else
+    {
+        print_all();
+    }
+    
+    port_dev_exit();
+
+    return 0;
+}

+ 4 - 0
boot_test/EcIO/readme.txt

@@ -0,0 +1,4 @@
+监控转速和温度
+sudo ./suguang_fan.py
+调整转速:
+sudo ./suguang_fan_set.py

+ 34 - 0
boot_test/EcIO/suguang_fan.py

@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+import os, time, sys
+
+FAN1 = (0x67, 0x68)
+FAN2 = (0x6B, 0x6C)
+TEMP_ADDR = 0x65
+
+def read_reg(add):
+    cmd = "./ecx86_64 -a %s -r" % hex(add)
+    f = os.popen(cmd)
+    return int(f.read(), 16)
+
+
+def main():
+    while True:
+        f1_h = read_reg(FAN1[0])
+        f1_l = read_reg(FAN1[1])
+        f1_speed = ((f1_h & 0xFF) << 8) | (f1_l & 0xFF)
+
+        f2_h = read_reg(FAN2[0])
+        f2_l = read_reg(FAN2[1])
+        f2_speed = ((f2_h & 0xFF) << 8) | (f2_l & 0xFF)
+        
+        temp = read_reg(TEMP_ADDR)
+        
+        sys.stdout.write("\r")
+        sys.stdout.write("temp: %s fan1 rpm: %s \t fan2 rpm: %s           " % (temp, f1_speed, f2_speed))
+        sys.stdout.flush()
+        time.sleep(1)
+
+
+if __name__ == '__main__':
+    os.system("chmod +x ./ecx86_64")
+    main()

+ 28 - 0
boot_test/EcIO/suguang_fan_set.py

@@ -0,0 +1,28 @@
+#!/usr/bin/python3
+import os, time, sys
+
+FAN1 = (0x67, 0x68)
+FAN2 = (0x6B, 0x6C)
+
+PWN = 0x0D
+TEST_EN = 0x0E
+
+
+def read_reg(add):
+    cmd = "./ecx86_64 -a %s -r" % hex(add)
+    f = os.popen(cmd)
+    return int(f.read(), 16)
+
+
+def main():
+    while True:
+        sval = input("请输入风扇转速等级(1~254):")
+        val = int(sval)
+        os.system("./ecx86_64 -a %s -w %s" % (hex(PWN), hex(val)))
+
+
+
+if __name__ == '__main__':
+    os.system("chmod +x ./ecx86_64")
+    os.system("./ecx86_64 -a %s -w %s" % (hex(TEST_EN), 1))
+    main()

BIN
boot_test/EcIO/suguang_fan_tool.tar.xz


BIN
boot_test/EcIO/suguang_fan_tool_v2.tar.xz


BIN
boot_test/RunTest


+ 178 - 0
boot_test/RunTest.sh

@@ -0,0 +1,178 @@
+#!/bin/bash
+MYDIR=$(dirname $(readlink -f "$0"))
+if [ $UID -ne 0 ]; then
+    echo "请以root用户身份运行!"
+    echo "提示: sudo -s 或 su root 可以切换到root用户"
+    exit 1
+fi
+
+ARCH=$(arch)
+
+usage()
+{
+    echo "输入错误!"
+    echo "支持:睡眠(suspend)/休眠(hibernate)/重启(warnboot)/冷重启(coldboot)测试。"
+    echo "用法:./RunTest [suspend/hibernate/warnboot/coldboot] [次数]"
+    echo "例如: 循环300次重启测试: ./RunTest.sh warnboot 300"
+    exit 1
+}
+
+if [ $# -lt 2 ]; then
+    usage
+fi
+
+
+info(){
+		# 获取key所在的行
+        info_line=`sed -n "/^$1=/p" $2`
+        if [[ -z $info_line ]];then
+                echo ""
+        fi
+		# 返回key对应的value值
+        echo $info_line|awk -F '=' '{print $2}'
+}
+
+install_ec_tools()
+{
+    if [ ! -f "/proc/ec_control/resume_timer" ];then
+        cd EcTools
+        insmod ./ec_operation.ko >> /dev/null 2>&1
+        if [ ! -f "/proc/ec_control/resume_timer" ];then
+            echo "install ec tools ..."
+            make >> $login_user_home/debug.log 2>&1
+            insmod ./ec_operation.ko >> $login_user_home/debug.log
+            if [ $? -ne 0 ];then
+                echo "ERROR: install ec tools fail"
+                read
+                exit 1
+            fi
+        else
+            echo "ec tools ready"
+        fi
+        cd $MYDIR
+    fi
+}
+
+set_autorun_uos()
+{
+    #设置关闭自动更新
+    echo 设置关闭自动更新
+    dbus-send --system --dest=com.deepin.lastore --type=method_call /com/deepin/lastore com.deepin.lastore.Updater.SetAutoCheckUpdates boolean:false > /dev/null 2>&1
+    #设置关闭更新提醒
+    dbus-send --system --dest=com.deepin.lastore --type=method_call /com/deepin/lastore com.deepin.lastore.Updater.SetUpdateNotify boolean:false > /dev/null 2>&1
+    # set autologin
+    sed -i 's/^#\(autologin-user=\)$/\1'${login_user_name}'/' /etc/lightdm/lightdm.conf || echo "设置自动登陆失败,请手动设置自动登陆,已经设置请忽略"
+    echo "更改系统设置。。。"
+    su $login_user_name <<EOF
+bash $MYDIR/set_power_plan.sh;
+exit;
+EOF
+    cat > $login_user_home/.config/autostart/reboot.desktop <<EOF
+[Desktop Entry]
+Type=Application
+Exec=$MYDIR/reboot.sh
+Name=运行量产测试
+StartupNotify=false
+Terminal=true
+EOF
+}
+
+set_autorun_kylin()
+{
+    sed -i "\$a\*\/${interval:=2} * * * * root cd \/${MYDIR} && .\/reboot.sh" /etc/crontab
+    # sed -i "\$a\*\/${interval:=2} * * * * root cd \/${MYDIR} && DISPLAY=:0.0 mate-terminal -e .\/reboot.sh" /etc/crontab
+}
+
+login_user_id=$(grep '^\<UID_MIN\>' /etc/login.defs | awk '{print $2}')
+login_user_name=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $1}')
+login_user_home=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $6}')
+
+case "$ARCH" in
+    mips64)
+        # ifconfig enp1s0 down > /dev/null 2>&1
+        # # 关闭WiFi
+        # rfkill block 0 > /dev/null 2>&1
+        ;;
+    
+    loongarch64)
+        # ifconfig enp1s0 down > /dev/null 2>&1
+        # # 关闭WiFi
+        # rfkill block 0 > /dev/null 2>&1
+        ;;
+
+    aarch64)
+        echo ""
+        # ifconfig enp3s0 down > /dev/null 2>&1
+        # # 关闭WiFi
+        # rfkill block 0 > /dev/null 2>&1
+        ;;
+    x86_64)
+        chmod +x ec$ARCH
+        ;;
+    *)
+        echo "未支持 $ARCH 架构,测试未进行!"
+        exit 1
+    ;;
+esac
+
+date >> $login_user_home/debug.log
+
+
+chmod +x *.sh
+chmod +x rtc_resume rtc_resume_la
+# chmod +x mem
+chmod +x ec*
+
+if [ $2 -le 0 ]; then
+    echo "输入有误! 循环次数必须大于0 请重新运行"
+    usage
+fi
+LOG_FILE=$1`date +%Y%m%d`.log
+
+echo "日志文件: ${LOG_FILE}"
+
+TIMECTRL=`date '+%s'`
+sed -i "s#^TIMECTRL=.*#TIMECTRL=$TIMECTRL#" ./action.cfg
+sed -i "s#^LOG=.*#LOG=$LOG_FILE#" ./action.cfg
+sed -i "s#^ACTION=.*#ACTION=$1#" ./action.cfg
+echo "timesLeft=$2" > ./times.left
+
+OS=`info ID /etc/os-release`
+if [ "$OS" == "uos" ];then
+    set_autorun=set_autorun_uos
+else
+    set_autorun=set_autorun_kylin
+fi
+
+echo "测试即将开始。。。"
+
+source "${MYDIR}/times.left"
+source "${MYDIR}/action.cfg"
+interval=2
+echo "TIMER=${TIMER} " >> $login_user_home/debug.log
+echo "TIMECTRL=${TIMECTRL}" >> $login_user_home/debug.log
+echo "LOG=${LOG}" >> $login_user_home/debug.log
+chown $login_user_name:$login_user_name $login_user_home/debug.log
+case "$1" in
+suspend)
+    ./S3S4.sh
+    exit 0
+    ;;
+warnboot)
+	$set_autorun
+    chown $login_user_name:$login_user_name $MYDIR -R
+    ./reboot.sh
+    ;;
+hibernate)
+    chown $login_user_name:$login_user_name $MYDIR -R
+    ./S3S4.sh
+    ;;
+coldboot)
+    $set_autorun
+    ./reboot.sh
+    ;;
+    *)
+    usage
+    ;;
+esac
+

+ 178 - 0
boot_test/S3S4.sh

@@ -0,0 +1,178 @@
+#!/bin/bash
+MYDIR=$(dirname $(readlink -f "$0"))
+echo "dir: ${MYDIR}"  >> /tmp/debug.log
+login_user_id=$(grep '^\<UID_MIN\>' /etc/login.defs | awk '{print $2}')
+login_user_name=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $1}')
+login_user_home=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $6}')
+source "${MYDIR}/times.left"
+source "${MYDIR}/action.cfg"
+logFile=${MYDIR}/${LOG}
+count=${timesLeft}
+echo "[`date`] 开始待机${ACTION}测试。。。" | tee -a ${logFile}
+# echo "[`date`] 开始待机${ACTION}测试。。。" >> result.txt
+echo "日志文件: ${logFile}"
+echo "循环次数: ${count}"  | tee -a ${logFile}
+ARCH=$(arch)
+#ARCH="loongarch64"
+console=/dev/ttyS0
+
+if [ $ARCH = "aarch64" ]; then
+    console=/dev/ttyAMA0
+fi
+
+ec_wakeup()
+{
+    echo "write EC[0x0F] = 0x28"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x28 -a 0x0F
+    sleep 0.5
+    echo -ne "read EC[0x0F]:"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x0F  >> ${logFile}
+}
+
+
+loongson_suspend()
+{
+    if [ "$ARCH" == "mips64" ]; then
+        ${MYDIR}/rtc_resume ${TIMER}s
+    else
+        # echo "set_rtc rtc_resume_la ${TIMER}s"
+        ec_wakeup
+        sleep 1
+    fi
+    # hwclock -w
+	# freeze mem disk
+    echo mem > /sys/power/state
+}
+
+loongson_hibernate()
+{
+    ec_wakeup
+    echo disk > /sys/power/state
+}
+
+uos_suspend()
+{
+    rtcwake -m mem -s 60
+    # hwclock -w
+    #echo mem > /sys/power/state
+}
+
+
+uos_hibernate()
+{
+    #ec_wakeup
+    #echo disk > /sys/power/state
+    rtcwake -m disk -s 60
+}
+
+haiguagn_suspend()
+{
+    ec_wakeup
+    systemctl suspend
+}
+
+haiguang_hibernate()
+{
+    ec_wakeup
+    systemctl hibernate
+}
+
+phytium_suspend()
+{
+    ec_wakeup
+
+    echo "write EC[0x0C] = 0x0F"  >> ${logFile}  #aarch64
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x0F -a 0x08
+    #echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x01 -a 0x0A
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x0F -a 0x08
+    #echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x01 -a 0x0A
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x0F -a 0x08
+    #echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x01 -a 0x0A
+    sleep 0.5
+    echo -ne "read EC[0x08]:"  >> ${logFile}
+    #echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x0C  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x08  >> ${logFile}
+    systemctl suspend
+    #rtcwake -m mem -s 5
+    #sleep 60
+    ##ec_wakeup
+}
+
+phytium_hibernate()
+{
+    ec_wakeup
+    echo "write EC[0x1E] = 0x08"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo -ne "read EC[0x08]:"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x08  >> ${logFile}
+    systemctl hibernate
+    #sleep 60
+    #ec_wakeup
+}
+
+case "$ARCH" in
+    mips64)
+        suspend_action=loongson_suspend
+        hibernate_action=loongson_hibernate
+        ;;
+    loongarch64)
+        suspend_action=loongson_suspend
+        hibernate_action=loongson_hibernate
+        ;;
+    aarch64)
+        suspend_action=phytium_suspend
+        hibernate_action=phytium_hibernate
+        ;;
+    x86_64)
+        #suspend_action=haiguagn_suspend
+        #hibernate_action=haiguang_hibernate
+		suspend_action=uos_suspend
+		hibernate_action=uos_hibernate
+        ;;
+    *)
+        echo "未支持$ARCH架构,测试未进行!"
+        exit 1
+        ;;
+esac
+
+
+while [ $count -gt 0 ]
+do
+    echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} remaining ${count} times" > $console
+    echo "还剩余${count}次${ACTION}测试"
+    echo "[`date`] 还剩余${count}次${ACTION}测试" >> ${logFile}
+    if read -t 30 -p "按Enter键停止测试!" start; then
+        chown $login_user_name:$login_user_name $MYDIR -R
+        echo "测试中止!"
+        # ifconfig enp1s0 up
+        # rfkill unblock 0
+        exit 1
+    fi
+    s=$(printf "%-30s" "=")
+    echo -e "${s// /=}\n" >> ${logFile}
+    echo -e "${s// /=}\n" >> ${logFile}
+    count=$(($count-1))
+    case "${ACTION}" in
+    suspend)
+        $suspend_action
+        ;;
+    hibernate)
+        $hibernate_action
+        ;;
+    esac
+    echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} finish!" > $console
+    clear
+done
+echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} all done!" > $console
+echo "[`date`] ${ACTION}测试完成" | tee -a ${logFile}
+# echo "[`date`] ${count}次${ACTION}测试完成" >> result.txt
+chown $login_user_name:$login_user_name $MYDIR -R
+read input
+exit 0

+ 5 - 0
boot_test/action.cfg

@@ -0,0 +1,5 @@
+ACTION=suspend
+TIMER=30
+TIMECTRL=5775
+LOG=suspend19700101.log
+

BIN
boot_test/ecaarch64


BIN
boot_test/ecloongarch64


BIN
boot_test/ecmips64


BIN
boot_test/ecx86_64


+ 184 - 0
boot_test/grub.cfg

@@ -0,0 +1,184 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically generated by grub-mkconfig using templates
+# from /etc/grub.d and settings from /etc/default/grub
+#
+
+### BEGIN /etc/grub.d/00_header ###
+if [ -s $prefix/grubenv ]; then
+  set have_grubenv=true
+  load_env
+fi
+if [ "${next_entry}" ] ; then
+   set default="${next_entry}"
+   set next_entry=
+   save_env next_entry
+   set boot_once=true
+else
+   set default="0"
+fi
+
+if [ x"${feature_menuentry_id}" = xy ]; then
+  menuentry_id_option="--id"
+else
+  menuentry_id_option=""
+fi
+
+export menuentry_id_option
+
+if [ "${prev_saved_entry}" ]; then
+  set saved_entry="${prev_saved_entry}"
+  save_env saved_entry
+  set prev_saved_entry=
+  save_env prev_saved_entry
+  set boot_once=true
+fi
+
+function savedefault {
+  if [ -z "${boot_once}" ]; then
+    saved_entry="${chosen}"
+    save_env saved_entry
+  fi
+}
+function load_video {
+  if [ x$feature_all_video_module = xy ]; then
+    insmod all_video
+  else
+    insmod efi_gop
+    insmod efi_uga
+    insmod ieee1275_fb
+    insmod vbe
+    insmod vga
+    insmod video_bochs
+    insmod video_cirrus
+  fi
+}
+
+if [ x$feature_default_font_path = xy ] ; then
+   font=unicode
+else
+set root='hd0,gpt3'
+if [ x$feature_platform_search_hint = xy ]; then
+  search --no-floppy --fs-uuid --set=root --hint-ieee1275='ieee1275//disk@0,gpt3' --hint-bios=hd0,gpt3 --hint-efi=hd0,gpt3 --hint-baremetal=ahci0,gpt3  19054f86-3cc7-4f42-9532-edb4a044aa4c
+else
+  search --no-floppy --fs-uuid --set=root 19054f86-3cc7-4f42-9532-edb4a044aa4c
+fi
+    font="/usr/share/grub/unicode.pf2"
+fi
+set gfxmode=auto
+if loadfont $font ; then
+echo
+fi
+terminal_output gfxterm
+if [ "${recordfail}" = 1 ] ; then
+  set timeout=30
+else
+  if [ x$feature_timeout_style = xy ] ; then
+    set timeout_style=menu
+    set timeout=5
+  # Fallback normal timeout code in case the timeout_style feature is
+  # unavailable.
+  else
+    set timeout=5
+  fi
+fi
+### END /etc/grub.d/00_header ###
+
+### BEGIN /etc/grub.d/01_live ###
+if [ ! -z ${boot_once} ];then
+  set timeout_style=countdown
+  set load_config=load-config
+else
+  set load_config=
+fi
+### END /etc/grub.d/01_live ###
+
+### BEGIN /etc/grub.d/05_debian_theme ###
+### END /etc/grub.d/05_debian_theme ###
+
+### BEGIN /etc/grub.d/10_linux ###
+function gfxmode {
+	set gfxpayload="${1}"
+}
+set linux_gfx_mode=
+export linux_gfx_mode
+menuentry 'UnionTech OS Desktop 20 Professional GNU/Linux' --class uniontech --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-19054f86-3cc7-4f42-9532-edb4a044aa4c' {
+	set root='hd0,gpt2'
+	if [ x$feature_platform_search_hint = xy ]; then
+	  search --no-floppy --fs-uuid --set=root --hint-ieee1275='ieee1275//disk@0,gpt2' --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2  fcbaa12f-8584-4f02-aff5-adbcd762bfed
+	else
+	  search --no-floppy --fs-uuid --set=root fcbaa12f-8584-4f02-aff5-adbcd762bfed
+	fi
+if [ loongson != "loongson" ];then
+	echo	'Loading Linux 4.19.0-loongson-3-desktop ...'
+fi
+	linux	/vmlinuz-4.19.0-loongson-3-desktop root=UUID=19054f86-3cc7-4f42-9532-edb4a044aa4c ro  splash quiet console=tty loglevel=0
+if [ loongson != "loongson" ];then
+	echo	'Loading initial ramdisk ...'
+fi
+	initrd	/initrd.img-4.19.0-loongson-3-desktop
+    boot 
+}
+submenu 'Advanced options for UnionTech OS Desktop 20 Professional GNU/Linux' $menuentry_id_option 'gnulinux-advanced-19054f86-3cc7-4f42-9532-edb4a044aa4c' {
+	menuentry 'UnionTech OS Desktop 20 Professional GNU/Linux, with Linux 4.19.0-loongson-3-desktop' --class uniontech --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-4.19.0-loongson-3-desktop-advanced-19054f86-3cc7-4f42-9532-edb4a044aa4c' {
+		set root='hd0,gpt2'
+		if [ x$feature_platform_search_hint = xy ]; then
+		  search --no-floppy --fs-uuid --set=root --hint-ieee1275='ieee1275//disk@0,gpt2' --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2  fcbaa12f-8584-4f02-aff5-adbcd762bfed
+		else
+		  search --no-floppy --fs-uuid --set=root fcbaa12f-8584-4f02-aff5-adbcd762bfed
+		fi
+	if [ loongson != "loongson" ];then
+		echo	'Loading Linux 4.19.0-loongson-3-desktop ...'
+	fi
+		linux	/vmlinuz-4.19.0-loongson-3-desktop root=UUID=19054f86-3cc7-4f42-9532-edb4a044aa4c ro  splash quiet console=tty loglevel=0
+	if [ loongson != "loongson" ];then
+		echo	'Loading initial ramdisk ...'
+	fi
+		initrd	/initrd.img-4.19.0-loongson-3-desktop
+	    boot 
+	}
+}
+
+### END /etc/grub.d/10_linux ###
+
+### BEGIN /etc/grub.d/11_deepin_ab_recovery ###
+### END /etc/grub.d/11_deepin_ab_recovery ###
+
+### BEGIN /etc/grub.d/15_linux_bar ###
+menuentry 'Uniontech OS Backup & Restore' --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-recovery' {
+	set root='hd0,gpt6'
+	if [ x$feature_platform_search_hint = xy ]; then
+	  search --no-floppy --fs-uuid --set=root --hint-ieee1275='ieee1275//disk@0,gpt6' --hint-bios=hd0,gpt6 --hint-efi=hd0,gpt6 --hint-baremetal=ahci0,gpt6  de157839-4e13-4ba0-aee2-f46b8a4730e6
+	else
+	  search --no-floppy --fs-uuid --set=root de157839-4e13-4ba0-aee2-f46b8a4730e6
+	fi
+	linux	/doppel/vmlinuz-4.19.90 ro boot=live $load_config components toram=filesystem.squashfs locales=zh_CN.UTF-8 acpi_backlight=vendor splash quiet console=tty loglevel=0 live-media-path=doppel
+	initrd	/doppel/initrd.img-4.19.90
+	boot
+}
+
+### END /etc/grub.d/15_linux_bar ###
+
+### BEGIN /etc/grub.d/20_linux_xen ###
+### END /etc/grub.d/20_linux_xen ###
+
+### BEGIN /etc/grub.d/30_os-prober ###
+### END /etc/grub.d/30_os-prober ###
+
+### BEGIN /etc/grub.d/30_uefi-firmware ###
+### END /etc/grub.d/30_uefi-firmware ###
+
+### BEGIN /etc/grub.d/40_custom ###
+# This file provides an easy way to add custom menu entries.  Simply type the
+# menu entries you want to add after this comment.  Be careful not to change
+# the 'exec tail' line above.
+### END /etc/grub.d/40_custom ###
+
+### BEGIN /etc/grub.d/41_custom ###
+if [ -f  ${config_directory}/custom.cfg ]; then
+  source ${config_directory}/custom.cfg
+elif [ -z "${config_directory}" -a -f  $prefix/custom.cfg ]; then
+  source $prefix/custom.cfg;
+fi
+### END /etc/grub.d/41_custom ###

+ 146 - 0
boot_test/reboot.sh

@@ -0,0 +1,146 @@
+#!/bin/bash
+login_user_id=$(grep '^\<UID_MIN\>' /etc/login.defs | awk '{print $2}')
+login_user_name=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $1}')
+login_user_home=$(cat /etc/passwd | awk -v id="$login_user_id" -F: '{if ($3==id) print $6}')
+# echo  $login_user_id
+# echo  $login_user_name
+# echo  $login_user_home
+
+MYDIR=$(dirname $(readlink -f "$0"))
+echo "dir: ${MYDIR}"  >> /tmp/debug.log
+
+echo "${MYDIR}/times.left" >> /tmp/debug.log
+source "${MYDIR}/times.left"
+source "${MYDIR}/action.cfg"
+logFile=${MYDIR}/${LOG}
+ARCH=$(arch)
+# resultFile=$(dirname "${MYDIR}")/result/result.txt
+console=/dev/ttyS0
+
+if [ $ARCH = "aarch64" ]; then
+    console=/dev/ttyAMA0
+fi
+s=$(printf "%-30s" "=")
+echo -e "${s// /=}\n" >> ${logFile}
+now=`date '+%s'`
+if [ $(($now - $TIMECTRL)) -ge 300 ]; then
+    echo "[`date`] 警告:距离上次自动执行超5分钟"  | tee -a ${logFile}
+    # echo -e "检测到超时(5分钟)异常,测试终止!\n $now - $TIMECTRL -ge 300" >> /tmp/debug.log
+    # rm -f $login_user_home/.config/autostart/reboot.desktop
+    # chown $login_user_name:$login_user_name $MYDIR -R
+    # exit 1
+fi
+# export PATH=$PATH:$MYDIR
+
+ec_wakeup()
+{
+    echo "write EC[0x08] = 0x28"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x28 -a 0x08
+    sleep 0.5
+    echo -ne "read EC[0x08]:"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x08  >> ${logFile}
+}
+
+loongson_coldboot()
+{
+    ec_wakeup
+}
+
+
+phytium_coldboot2()
+{
+    #ibf write 66 bit 1 = 0  read obf 66 bit 0 = 0  
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000066 0x81 > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0xd > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0x1 > /dev/null
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000066 0x81 > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0xd > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0x1 > /dev/null
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000066 0x81 > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0xd > /dev/null
+    sleep 0.1
+    echo lc111111 | sudo -S ${MYDIR}/mem write 1 0x20000062 0x1 > /dev/null
+    sleep 0.5
+}
+
+phytium_coldboot()
+{
+    echo "write EC[0x08] = 0x1E"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -w 0x1E -a 0x08
+    sleep 0.5
+    echo -ne "read EC[0x0D]:"  >> ${logFile}
+    echo lc111111 | sudo -S ${MYDIR}/ec$ARCH -r -a 0x08  >> ${logFile}
+}
+
+case "$ARCH" in
+    mips64)
+        coldboot_action=loongson_coldboot
+        ;;
+    aarch64)
+        coldboot_action=phytium_coldboot
+        ;;
+    *)
+        coldboot_action=ec_wakeup
+        # echo "未支持$ARCH架构,测试未进行!"
+        # exit 1
+    ;;
+esac
+
+info(){
+		# 获取key所在的行
+        info_line=`sed -n "/^$1=/p" $2`
+        if [[ -z $info_line ]];then
+                echo ""
+        fi
+		# 返回key对应的value值
+        echo $info_line|awk -F '=' '{print $2}'
+}
+
+OS=`info ID /etc/os-release`
+
+
+if [ ${timesLeft} -gt '0' ];then
+    echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} remaining ${timesLeft} times" > $console
+    echo "[`date`]还剩余${timesLeft}次${ACTION}。" | tee -a ${logFile}
+    ############################################################
+    if [ "$OS" == "uos" ];then
+        if read -t 15 -p "按Enter键停止测试!" start; then
+            rm -f $login_user_home/.config/autostart/reboot.desktop
+            chown $login_user_name:$login_user_name $MYDIR -R
+            echo "测试中止!"
+            exit 1
+        fi
+    fi
+    echo "timesLeft=$((timesLeft-1))" > ${MYDIR}/times.left  
+    now=`date '+%s'`
+    sed -i "s#^TIMECTRL=.*#TIMECTRL=$now#" ${MYDIR}/action.cfg
+    chown $login_user_name:$login_user_name $MYDIR -R
+    case "${ACTION}" in
+    warnboot)
+        /sbin/reboot
+        ;;
+    coldboot)
+        $coldboot_action
+        /sbin/poweroff
+        ;;
+    esac
+    echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} finish!" > $console
+else
+    echo "[`date "+%m-%d-%H:%M:%S"`] Do ${ACTION} all done!" > $console
+    echo "[`date`] ${ACTION}测试完成" | tee -a ${logFile}
+    rm -f $login_user_home/.config/autostart/reboot.desktop
+    echo lc111111 | sudo -S sed -i '/.\/reboot.sh/d' /etc/crontab
+    chown $login_user_name:$login_user_name $MYDIR -R
+    read i
+fi

BIN
boot_test/rtc_resume


BIN
boot_test/rtc_resume_la


+ 24 - 0
boot_test/script.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+echo $(readlink -f "$0")
+echo $0
+if [ "$1" != "cron" ]; then
+    echo "cron start"
+fi
+
+info(){
+		# 获取key所在的行
+        info_line=`sed -n "/^$1=/p" $2`
+        if [[ -z $info_line ]];then
+                echo ""
+        fi
+		# 返回key对应的value值
+        echo $info_line|awk -F '=' '{print $2}'
+}
+
+OS=`info ID /etc/os-release`
+if [ "$OS" == "ubuntu" ];then
+    #设置关闭自动更新
+    echo 设置关闭自动更新
+
+fi
+echo "done"

+ 17 - 0
boot_test/set_power_plan.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+echo "set power plan"
+echo -ne "user: "
+echo $(whoami)
+# 不关屏
+gsettings set com.deepin.dde.power line-power-screen-black-delay 0
+gsettings set com.deepin.dde.power battery-screen-black-delay 0
+# 不睡眠
+gsettings set com.deepin.dde.power line-power-sleep-delay 0
+gsettings set com.deepin.dde.power battery-sleep-delay 0
+# 不锁屏
+gsettings set com.deepin.dde.power line-power-lock-delay 0
+gsettings set com.deepin.dde.power battery-lock-delay 0
+# 唤醒无密码
+gsettings set com.deepin.dde.power sleep-lock false
+# 锁屏无密码
+gsettings set com.deepin.dde.power screen-black-lock false

+ 12 - 0
boot_test/test.sh

@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+#
+
+#confirm pc permission
+pc=`echo "lc111111" | sudo -S ls`
+sudo ./RunTest.sh $1 $2
+
+
+
+
+

+ 1 - 0
boot_test/times.left

@@ -0,0 +1 @@
+timesLeft=3

BIN
boot_test/wav/coldboot.wav


BIN
boot_test/wav/hibernate.wav


BIN
boot_test/wav/suspend.wav


BIN
boot_test/wav/warnboot.wav


+ 9 - 0
boot_test/使用说明.txt

@@ -0,0 +1,9 @@
+支持:睡眠(suspend)/休眠(hibernate)/重启(warnboot)/冷重启(coldboot)测试。
+
+进入RunTest.sh所在目录在空白处鼠标右键先选择‘在终端打开’
+先输入sudo -s 切换到root的用户 
+用法:./RunTest [suspend/hibernate/warnboot/coldboot] [次数]
+例如: 循环300次重启测试: ./RunTest.sh warnboot 300
+
+结果查看:
+测试会在当前目录产生相应的日志文件,例如重启的日志:warnboot20200429.log

+ 55 - 0
boot_test/更改历史.txt

@@ -0,0 +1,55 @@
+######################################
+2021/09/07 v9.9
+飞腾S4/S5测试EC唤醒地址变更 0x0D -> 0x17
+
+######################################
+2021/08/24 v9.5 
+增加向串口输出测试进度信息
+
+######################################
+2021/06/29 v9.4 
+龙芯3A5000平台支持S3改用EC唤醒,rtc唤醒有概率不能自动唤醒
+
+######################################
+2021/04/17 v9 
+1. 兼容支持uos系统和麒麟系统
+2. 修复飞腾平台S4/coldboot 有概念出现不能自动唤醒
+3. 新增龙芯3A5000平台支持
+
+
+######################################
+2021/03/25 V8.2
+1.FIX uos 系统下warnboot没有设置自动登陆
+
+######################################
+2021/03/25 V8.1
+1.FIX 曙光一体机麒麟系统不能运行问题
+
+######################################
+2021/03/03 v8
+1.添加曙光一体机coldboot支持, S3/S4测试该版本未支持
+
+######################################
+2021/01/09 v7.1
+1.fix飞腾平台oldboot 不能跑
+
+######################################
+2021/01/09 v7
+1.添加对飞腾平台的支持
+2.reboot/coldboot超时现在只是警告,测试将继续进行
+
+######################################
+2020/09/18 v6
+暂时关闭不稳定的cron计划任务,修复重启测试脚本重复运行导致出错
+
+######################################
+2020/09/17 v5
+修復warnboot coldboot不計數,coldboot 安裝ec_operation.ko 失敗問題
+############################################
+2020/08/28 v3.1
+1.修复重启测试中途退出问题
+    重启测试取消timeout检查 
+    重启测试添加cron计划任务,双重保护
+2.关闭自动更新
+3.关闭WiFi
+4.关闭以太网

+ 12 - 0
main.cpp

@@ -0,0 +1,12 @@
+#include "ui/MainWindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[]) {
+  QApplication a(argc, argv);
+  a.setApplicationName("RunTest");
+
+  MainWindow w;
+  w.show();
+  return a.exec();
+}

+ 250 - 0
src/ui/MainWindow.cpp

@@ -0,0 +1,250 @@
+#include "MainWindow.h"
+#include "utils/StyleHelper.h"
+
+#include <QApplication>
+#include <QGroupBox>
+#include <QHBoxLayout>
+#include <QScrollBar>
+#include <QVBoxLayout>
+
+MainWindow::MainWindow(QWidget *parent)
+    : QMainWindow(parent), m_process(new QProcess(this)) {
+  initUI();
+
+  // 信号连接
+  connect(m_bootTypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
+          this, &MainWindow::onBootTypeChanged);
+  connect(m_numberInput, &QLineEdit::textChanged, this,
+          &MainWindow::onNumberChanged);
+  connect(m_executeBtn, &QPushButton::clicked, this,
+          &MainWindow::onExecuteClicked);
+  connect(m_process, &QProcess::readyReadStandardOutput, this,
+          &MainWindow::onProcessOutput);
+  connect(m_process, &QProcess::readyReadStandardError, this,
+          &MainWindow::onProcessOutput);
+  connect(m_process,
+          QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this,
+          &MainWindow::onProcessFinished);
+}
+
+MainWindow::~MainWindow() {
+  if (m_process->state() != QProcess::NotRunning) {
+    m_process->kill();
+    m_process->waitForFinished(3000);
+  }
+}
+
+void MainWindow::initUI() {
+  setWindowTitle(QString::fromUtf8("RunTest 命令启动器"));
+  setMinimumSize(700, 550);
+  resize(800, 600);
+
+  // 应用全局样式
+  qApp->setStyleSheet(StyleHelper::getGlobalStyle());
+
+  // 根 Widget
+  QWidget *rootWidget = new QWidget(this);
+  setCentralWidget(rootWidget);
+  QVBoxLayout *rootLayout = new QVBoxLayout(rootWidget);
+  rootLayout->setContentsMargins(24, 24, 24, 24);
+  rootLayout->setSpacing(16);
+
+  // ================== 标题 ==================
+  QLabel *titleLabel = new QLabel(QString::fromUtf8("🖥  RunTest 命令启动器"));
+  titleLabel->setStyleSheet(
+      "font-size: 26px; font-weight: bold; color: #00d4ff; padding: 8px 0;");
+  titleLabel->setAlignment(Qt::AlignCenter);
+  rootLayout->addWidget(titleLabel);
+
+  // ================== 配置区域 ==================
+  QHBoxLayout *configLayout = new QHBoxLayout();
+  configLayout->setSpacing(16);
+
+  // -- 启动类型 --
+  QVBoxLayout *bootTypeLayout = new QVBoxLayout();
+  QLabel *bootTypeTitle = new QLabel(QString::fromUtf8("⚙  启动类型(必选)"));
+  bootTypeTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
+
+  m_bootTypeCombo = new QComboBox();
+  m_bootTypeCombo->addItem(QString::fromUtf8("— 请选择启动类型 —"), "");
+  m_bootTypeCombo->addItem("warnboot", "warnboot");
+  m_bootTypeCombo->addItem("coldboot", "coldboot");
+  m_bootTypeCombo->setStyleSheet(StyleHelper::getComboBoxStyle());
+  m_bootTypeCombo->setCurrentIndex(0);
+
+  bootTypeLayout->addWidget(bootTypeTitle);
+  bootTypeLayout->addWidget(m_bootTypeCombo);
+
+  // -- 数字参数 --
+  QVBoxLayout *numberLayout = new QVBoxLayout();
+  QLabel *numberTitle =
+      new QLabel(QString::fromUtf8("🔢  参数(手动输入数字)"));
+  numberTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
+
+  m_numberInput = new QLineEdit();
+  m_numberInput->setPlaceholderText(QString::fromUtf8("输入数字,例如 5"));
+  m_numberInput->setStyleSheet(StyleHelper::getInputStyle());
+
+  numberLayout->addWidget(numberTitle);
+  numberLayout->addWidget(m_numberInput);
+
+  configLayout->addLayout(bootTypeLayout, 1);
+  configLayout->addLayout(numberLayout, 1);
+  rootLayout->addLayout(configLayout);
+
+  // ================== 命令预览 ==================
+  QLabel *previewTitle = new QLabel(QString::fromUtf8("📋  命令预览"));
+  previewTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
+
+  m_commandPreview =
+      new QLabel(QString::fromUtf8("请先选择启动类型并输入数字"));
+  m_commandPreview->setStyleSheet(StyleHelper::getPreviewStyle());
+  m_commandPreview->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+  m_commandPreview->setMinimumHeight(50);
+  m_commandPreview->setTextInteractionFlags(Qt::TextSelectableByMouse);
+
+  rootLayout->addWidget(previewTitle);
+  rootLayout->addWidget(m_commandPreview);
+
+  // ================== 执行按钮 + 状态 ==================
+  QHBoxLayout *actionLayout = new QHBoxLayout();
+
+  m_executeBtn = new QPushButton(QString::fromUtf8("▶  执行命令"));
+  m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
+  m_executeBtn->setEnabled(false); // 初始禁用,需要先选择类型
+
+  m_statusLabel = new QLabel(QString::fromUtf8("⏳ 等待配置..."));
+  m_statusLabel->setStyleSheet(StyleHelper::getStatusReadyStyle());
+
+  actionLayout->addWidget(m_executeBtn);
+  actionLayout->addStretch();
+  actionLayout->addWidget(m_statusLabel);
+  rootLayout->addLayout(actionLayout);
+
+  // ================== 输出区域 ==================
+  QLabel *outputTitle = new QLabel(QString::fromUtf8("📟  命令输出"));
+  outputTitle->setStyleSheet(StyleHelper::getSectionTitleStyle());
+
+  m_outputArea = new QTextEdit();
+  m_outputArea->setReadOnly(true);
+  m_outputArea->setStyleSheet(StyleHelper::getOutputStyle());
+  m_outputArea->setPlaceholderText(
+      QString::fromUtf8("命令输出将显示在这里..."));
+
+  rootLayout->addWidget(outputTitle);
+  rootLayout->addWidget(m_outputArea, 1);
+}
+
+void MainWindow::updateCommandPreview() {
+  QString bootType = m_bootTypeCombo->currentData().toString();
+  QString number = m_numberInput->text().trimmed();
+
+  if (bootType.isEmpty()) {
+    m_commandPreview->setText(QString::fromUtf8("请先选择启动类型"));
+    m_executeBtn->setEnabled(false);
+    return;
+  }
+
+  QString cmd;
+  if (number.isEmpty()) {
+    cmd = QString("sudo ./RunTest.sh %1").arg(bootType);
+  } else {
+    cmd = QString("sudo ./RunTest.sh %1 %2").arg(bootType, number);
+  }
+
+  m_commandPreview->setText(cmd);
+  m_executeBtn->setEnabled(true);
+}
+
+void MainWindow::onBootTypeChanged() { updateCommandPreview(); }
+
+void MainWindow::onNumberChanged() { updateCommandPreview(); }
+
+void MainWindow::onExecuteClicked() {
+  if (m_process->state() != QProcess::NotRunning) {
+    // 正在运行 → 停止
+    m_process->kill();
+    m_statusLabel->setText(QString::fromUtf8("⛔ 已停止"));
+    m_statusLabel->setStyleSheet(StyleHelper::getStatusErrorStyle());
+    m_executeBtn->setText(QString::fromUtf8("▶  执行命令"));
+    m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
+    m_bootTypeCombo->setEnabled(true);
+    m_numberInput->setEnabled(true);
+    return;
+  }
+
+  // 清空输出
+  m_outputArea->clear();
+
+  QString bootType = m_bootTypeCombo->currentData().toString();
+  QString number = m_numberInput->text().trimmed();
+
+  // 构建参数列表
+  QStringList args;
+  args << "./RunTest.sh" << bootType;
+  if (!number.isEmpty()) {
+    args << number;
+  }
+
+  // 追加命令到输出区显示
+  QString fullCmd = "sudo " + args.join(" ");
+  m_outputArea->append(
+      QString::fromUtf8("<span style='color:#ffd700;'>$ %1</span>")
+          .arg(fullCmd));
+  m_outputArea->append("");
+
+  // 更新 UI 状态
+  m_statusLabel->setText(QString::fromUtf8("🔄 正在执行..."));
+  m_statusLabel->setStyleSheet(StyleHelper::getStatusRunningStyle());
+  m_executeBtn->setText(QString::fromUtf8("⏹  停止"));
+  m_executeBtn->setStyleSheet(StyleHelper::getStopButtonStyle());
+  m_bootTypeCombo->setEnabled(false);
+  m_numberInput->setEnabled(false);
+
+  // 启动进程
+  m_process->start("sudo", args);
+}
+
+void MainWindow::onProcessOutput() {
+  // 读取标准输出
+  QByteArray stdOut = m_process->readAllStandardOutput();
+  if (!stdOut.isEmpty()) {
+    m_outputArea->append(QString::fromUtf8(stdOut).trimmed());
+  }
+
+  // 读取标准错误
+  QByteArray stdErr = m_process->readAllStandardError();
+  if (!stdErr.isEmpty()) {
+    m_outputArea->append(QString("<span style='color:#f44336;'>%1</span>")
+                             .arg(QString::fromUtf8(stdErr).trimmed()));
+  }
+
+  // 自动滚动到底部
+  m_outputArea->verticalScrollBar()->setValue(
+      m_outputArea->verticalScrollBar()->maximum());
+}
+
+void MainWindow::onProcessFinished(int exitCode,
+                                   QProcess::ExitStatus exitStatus) {
+  // 恢复 UI
+  m_executeBtn->setText(QString::fromUtf8("▶  执行命令"));
+  m_executeBtn->setStyleSheet(StyleHelper::getExecuteButtonStyle());
+  m_bootTypeCombo->setEnabled(true);
+  m_numberInput->setEnabled(true);
+
+  if (exitStatus == QProcess::NormalExit && exitCode == 0) {
+    m_statusLabel->setText(QString::fromUtf8("✅ 执行完成 (exit code: 0)"));
+    m_statusLabel->setStyleSheet(StyleHelper::getStatusDoneStyle());
+    m_outputArea->append(QString::fromUtf8(
+        "\n<span style='color:#00ff88;'>--- 命令执行成功 ---</span>"));
+  } else {
+    m_statusLabel->setText(
+        QString::fromUtf8("❌ 执行失败 (exit code: %1)").arg(exitCode));
+    m_statusLabel->setStyleSheet(StyleHelper::getStatusErrorStyle());
+    m_outputArea->append(
+        QString::fromUtf8(
+            "\n<span style='color:#f44336;'>--- 命令执行失败 (exit code: "
+            "%1) ---</span>")
+            .arg(exitCode));
+  }
+}

+ 45 - 0
src/ui/MainWindow.h

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <QComboBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QMainWindow>
+#include <QProcess>
+#include <QPushButton>
+#include <QTextEdit>
+
+/**
+ * @brief RunTest 命令启动器主界面
+ *
+ * 提供启动类型选择(warnboot / coldboot)、数字参数输入、
+ * 命令预览和执行功能。
+ */
+class MainWindow : public QMainWindow {
+  Q_OBJECT
+
+public:
+  MainWindow(QWidget *parent = nullptr);
+  ~MainWindow();
+
+private slots:
+  void onBootTypeChanged();
+  void onNumberChanged();
+  void onExecuteClicked();
+  void onProcessOutput();
+  void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+
+private:
+  void initUI();
+  void updateCommandPreview();
+
+  // UI 组件
+  QComboBox *m_bootTypeCombo;
+  QLineEdit *m_numberInput;
+  QLabel *m_commandPreview;
+  QPushButton *m_executeBtn;
+  QTextEdit *m_outputArea;
+  QLabel *m_statusLabel;
+
+  // 进程
+  QProcess *m_process;
+};

+ 205 - 0
src/utils/StyleHelper.h

@@ -0,0 +1,205 @@
+#pragma once
+
+#include <QString>
+
+/**
+ * @brief 集中管理所有 QSS 样式 — 深色工业风格
+ */
+class StyleHelper {
+public:
+  /// 全局应用样式
+  static QString getGlobalStyle() {
+    return R"(
+      * {
+        font-family: "Segoe UI", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
+      }
+      QMainWindow {
+        background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
+          stop:0 #0f0f1a, stop:1 #1a1a2e);
+      }
+      QLabel {
+        color: #e0e0e0;
+      }
+    )";
+  }
+
+  /// 下拉框样式
+  static QString getComboBoxStyle() {
+    return R"(
+      QComboBox {
+        background: #1e1e3a;
+        color: #00d4ff;
+        border: 2px solid #2a2a4a;
+        border-radius: 8px;
+        padding: 12px 18px;
+        font-size: 18px;
+        font-weight: bold;
+        min-width: 200px;
+      }
+      QComboBox:hover {
+        border-color: #00d4ff;
+        background: #252550;
+      }
+      QComboBox:focus {
+        border-color: #00d4ff;
+        background: #252550;
+      }
+      QComboBox::drop-down {
+        subcontrol-origin: padding;
+        subcontrol-position: top right;
+        width: 36px;
+        border-left: 2px solid #2a2a4a;
+        border-top-right-radius: 8px;
+        border-bottom-right-radius: 8px;
+        background: #1e1e3a;
+      }
+      QComboBox::down-arrow {
+        width: 12px;
+        height: 12px;
+      }
+      QComboBox QAbstractItemView {
+        background: #1e1e3a;
+        color: #00d4ff;
+        border: 2px solid #2a2a4a;
+        selection-background-color: #00d4ff;
+        selection-color: #0f0f1a;
+        font-size: 18px;
+        padding: 4px;
+      }
+    )";
+  }
+
+  /// 输入框样式
+  static QString getInputStyle() {
+    return R"(
+      QLineEdit {
+        background: #1e1e3a;
+        color: #00ff88;
+        border: 2px solid #2a2a4a;
+        border-radius: 8px;
+        padding: 12px 18px;
+        font-size: 22px;
+        font-weight: bold;
+        font-family: "Consolas", "Courier New", monospace;
+      }
+      QLineEdit:hover {
+        border-color: #00ff88;
+      }
+      QLineEdit:focus {
+        border-color: #00ff88;
+        background: #252550;
+      }
+      QLineEdit::placeholder {
+        color: #555588;
+      }
+    )";
+  }
+
+  /// 命令预览样式
+  static QString getPreviewStyle() {
+    return R"(
+      background: #12122a;
+      color: #ffd700;
+      border: 2px solid #2a2a4a;
+      border-radius: 8px;
+      padding: 14px 20px;
+      font-size: 20px;
+      font-weight: bold;
+      font-family: "Consolas", "Courier New", monospace;
+    )";
+  }
+
+  /// 执行按钮样式(正常)
+  static QString getExecuteButtonStyle() {
+    return R"(
+      QPushButton {
+        background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+          stop:0 #00c853, stop:1 #00e676);
+        color: #0a0a1e;
+        border: none;
+        border-radius: 10px;
+        padding: 16px 40px;
+        font-size: 20px;
+        font-weight: bold;
+        min-width: 180px;
+      }
+      QPushButton:hover {
+        background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+          stop:0 #00e676, stop:1 #69f0ae);
+      }
+      QPushButton:pressed {
+        background: #00a844;
+      }
+      QPushButton:disabled {
+        background: #333355;
+        color: #666688;
+      }
+    )";
+  }
+
+  /// 停止按钮样式
+  static QString getStopButtonStyle() {
+    return R"(
+      QPushButton {
+        background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+          stop:0 #d32f2f, stop:1 #f44336);
+        color: #ffffff;
+        border: none;
+        border-radius: 10px;
+        padding: 16px 40px;
+        font-size: 20px;
+        font-weight: bold;
+        min-width: 180px;
+      }
+      QPushButton:hover {
+        background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+          stop:0 #f44336, stop:1 #ef5350);
+      }
+      QPushButton:pressed {
+        background: #b71c1c;
+      }
+    )";
+  }
+
+  /// 输出区域样式
+  static QString getOutputStyle() {
+    return R"(
+      QTextEdit {
+        background: #0a0a18;
+        color: #00ff88;
+        border: 2px solid #2a2a4a;
+        border-radius: 8px;
+        padding: 12px;
+        font-size: 14px;
+        font-family: "Consolas", "Courier New", monospace;
+        line-height: 1.4;
+      }
+    )";
+  }
+
+  /// 状态标签 — 就绪
+  static QString getStatusReadyStyle() {
+    return "font-size: 14px; font-weight: bold; color: #00d4ff; padding: 4px;";
+  }
+
+  /// 状态标签 — 运行中
+  static QString getStatusRunningStyle() {
+    return "font-size: 14px; font-weight: bold; color: #ffd700; padding: 4px;";
+  }
+
+  /// 状态标签 — 完成
+  static QString getStatusDoneStyle() {
+    return "font-size: 14px; font-weight: bold; color: #00ff88; padding: 4px;";
+  }
+
+  /// 状态标签 — 错误
+  static QString getStatusErrorStyle() {
+    return "font-size: 14px; font-weight: bold; color: #f44336; padding: 4px;";
+  }
+
+  /// 区域标题样式
+  static QString getSectionTitleStyle() {
+    return "font-size: 15px; font-weight: bold; color: #9CA3AF; padding: 2px "
+           "0;";
+  }
+};