UEFI学习(四)-SuperIo的访问

一、什么是Super I/O?


Super I/O 芯片也叫 I/O 芯片。在 486 以上档次的主板上都有 I/O控制电路。因为在南桥这样的高速设备和串行、并行接口、软盘驱动器及键盘鼠标等大量低速设备之间必定存在资源的不匹配,而需要经过转换和管理。而 Super I/O 芯片则完成了该功能。
通常在硬件监控芯片硬件监控芯片中会整合超级 I/O 功能,可用于监控受监控对象的电压、温度、转速等。主板在附件中会提供某种软件,它和主板上的硬件配合使用就能实现对电压、温度、风扇转速等的监控,一旦检测到这些参数超出设定的指标时,它会自动作出调整,以保护元件的安全。常见的温度控制芯片有 ADT7463 等等;通用的通用硬件监控芯片有 Winbond W83697HF 和 W83627HF , SMSC 的 LPC47M172 , ITE 的IT8705F、IT8703F,ASUS 的 AS99172F 等等,这些芯片通常还整合了对于温度的监控需与温度传感元件配合;对风扇电机转速的监控,则需与 CPU 的散热风扇配合使用。

SIO是一个半可定制化的芯片,怎么说是半可定制化呢?比如上电时序,这一部分就是固化好的,而可定制化部分则是逻辑设备(Logic Device)部分。接入电源后SIO便根据固化的程序开始运作,等待power button触发。按下power button后SIO开始跑上电时序,CPU Reset后BIOS才开始跑,此时BIOS给SIO配置的Logic Device也才生效。
原链接:https://ay123.net/mystudy/673/

二、我们要用SuperIo实现什么

本文使用的SuperIo为NCT5581D,需要注意的是不同的厂家SuperIo的访问方式与配置都会有差异。学习的资料为对应的datasheet和program guide。
对于NCT5581D我们的学习目标为:

  • 了解NCT5581D的访问机制,学习 SIO Logic Device 相关内容。编写 一个 UEFI App(SIO.EFI),用于读写 SIO Logical Device 寄存器。
    命令格式
    读: SIO.EFI ldn //显示 ldn 这个 Device 下面的 256 个寄存器值
    写: SIO.EFI ldn,offset,value
    //把 value 值写到 ldn 这个 Device,地址为 offset
  • 了解NCT5581D的GPIO功能,选择一个 GPIO 引脚,用 SIO.EFI 工具控制输出电平。万用表测量,验证效果
  • 了解NCT5581D的Hardware Monitor的相关内容与其功能,编写 HardWareMonit 工具(HWM.efi)。实现一些功能:
    1、读取 CPU 温度、风扇转速、CPU 电压。循环显示出来
    2、控制 CPU 风扇转。
  • 了解NCT5581D的WDT功能,编写看门狗测试工具(WDT.efi)。
    运行命令:
    SIO.EFI nSec
    //设置 WDT Timer 为 nSec 秒。 nSec 秒后系统自动重启;实时显示 Timer 剩余时间

三、NCT5581D的访问机制

若要了解NCT5581D的访问机制需要对datasheet的第7章内容进行学习,从datasheet中可以了解到NCT5581D使用一个特殊的协议来访问配置寄存器来设置不同类型的配置。NCT5581D有十多个逻辑设备(from Logical Device 1 to Logical Device 16)如果将所有逻辑设备配置寄存器映射到正常的PC地址空间,则需要很大的地址空间。所以,NCT5581D使用两个I/O地址(2Eh/2Fh或4Eh/4Fh)映射所有配置寄存器,2Eh/2Fh或4Eh/4Fh作为index(索引)/data(数据)对进行对superio内部寄存器的访问。我们可以通过,对2Eh写入地址,去选中相应寄存器;而对2Fh进行读写数据,来达到我们对superio内部寄存器的目的。需要注意的是在NCT5581D中有一组全局寄存器位于0h-2fh的索引处,其中包含了整个芯片的信息和配置。而访问单个逻辑设备的控制寄存器的方法也很简单。只需将所需的逻辑设备号写入全局寄存器07h。索引为30h或更高的后续访问将直接访问到逻辑设备寄存器。

在我们开始着手去编写应用去验证功能之前,我们还需要了解一下,NCT5581D内部寄存器的结构,以便对上述提到的内容有更好的理解。

在这里插入图片描述

(其中A25/A26是访问HardwareMonitor的IOspace的index/data对,后面会解释。)
下面列出的是NCT5581D的全局寄存器,也就是对应于上图中00h-ffh的寄存器

在这里插入图片描述

从上图中的备注中可以看到全局变量寄存器CR20h和CR21h(注:CR的意思是control register就是控制寄存器的意思)中保存的是Chip-ID的高八位和低八位。其中低八位可能会因为版本变更而有变化,但高八位总是不变的。
看到这里我们可以对NCT5581D有一个验证的设想,我们可以通过上面描述的方式对全局变量寄存器CR20h和CR21h进行读取然后对照上表进行验证是否正确获得相应寄存器的值,从而证明上述的superio访问方式是正确的可行的。

配置顺序

根据第7章的内容想要读取寄存器的值需要按照一定的配置顺序:

  1. Enter the Extended Function Mode.(进入扩展功能模式)
  2. Configure the configuration registers(配置寄存器)
  3. Exit the Extended Function Mode.(退出扩展功能模式)

①进入扩展功能模式

要将芯片置入扩展功能模式,必须连续两次0x87写入应用于扩展功能启用注册器(EFERs,即2Eh或4Eh)。

②配置寄存器

芯片选择逻辑设备,并通过功能索引扩展功能数据寄存器(EFIR)和扩展功能数据寄存器(EFDR)激活所需的逻辑设备。EFIR与EFER位于相同的地址,而EFDR位于地址(EFIR+1)。首先,将逻辑设备编号(即0x07)写入EFIR,然后将所需的逻辑设备的编号写入EFDR。如果要访问芯片(全局)控制注册器,则不需要执行此步骤。其次,将逻辑设备中所需的配置寄存器的地址写入EFIR,然后通过EFDR写入(或读取)所需的配置寄存器。

③退出扩展功能模式

要退出扩展功能模式,需要向EFER写入0xAA。一旦芯片退出扩展功能模式,它将处于正常运行模式,并准备进入配置模式。

至此,了解了上述内容我们便可以开始着手并去验证这个访问机制了。

  1. 参照UEFI学习(二),我们创建一个edkii的标准应用工程采用MdeModulePkg包来编译,文件夹名字取为mySio,包含mySio.c和mySio.inf;

  2. mySio.c中加入以下内容:

    #include <Uefi.h>
    #include <Library/UefiLib.h>
    #include <Library/UefiApplicationEntryPoint.h>
    #include <Library/IoLib.h>
    //edkii中已包含了对io地址读写的库IoLib.h,从而我们可以使用IoWrite8/IoRead8进行写操作和读操作,
    //具体函数定义还需跳转到IoLib.h去查看。
    #define  IoIndexPort 0x2E
    #define  IoDataPort  0x2F
    
    
    EFI_STATUS
    EFIAPI
    UefiMain (
      IN EFI_HANDLE        ImageHandle,
      IN EFI_SYSTEM_TABLE  *SystemTable
      )
    {
      UINT8  ID_High=0;
      UINT8  ID_Low=0;
      //进入扩展模式
      IoWrite8(IoIndexPort,0x87);
      IoWrite8(IoIndexPort,0x87);
      //读取全局寄存器读取ID号:高位+地位
      IoWrite8(IoIndexPort,0x20);
      ID_High=IoRead8(IoDataPort);
      IoWrite8(IoIndexPort,0x21);
      ID_Low=IoRead8(IoDataPort);
      Print(L"SuperIO ID is %X %X\n",ID_High,ID_Low);
      //退出扩展模式
      IoWrite8(IoIndexPort,0xAA);
      return EFI_SUCCESS;
    }
    
    
  3. mySio.inf中加入以下内容:

    [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = mySio
      FILE_GUID                      = c3d8fe10-29fb-48bd-bf61-6fda693d7712
      MODULE_TYPE                    = UEFI_APPLICATION
      VERSION_STRING                 = 1.0
      ENTRY_POINT                    = UefiMain
    
    [Sources]
      mySio.c
    
    [Packages]
      MdePkg/MdePkg.dec
      MdeModulePkg/MdeModulePkg.dec
    
    [LibraryClasses]
      UefiApplicationEntryPoint
      UefiLib
      IoLib
    
    
    
  4. IoLib.h的路径在edk2\MdePkg\Include\Library\IoLib.h,其中我们可以从中查看到IoWrite8/IoRead8两个函数的描述而对于其底层实现,我们现在可以暂时不用去深入了解:

    /**
      Reads an 8-bit I/O port.
    
      Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned.
      This function must guarantee that all I/O read and write operations are
      serialized.
    
      If 8-bit I/O port operations are not supported, then ASSERT().
    
      @param  Port  The I/O port to read.
    
      @return The value read.
    
    **/
    UINT8
    EFIAPI
    IoRead8 (
      IN      UINTN                     Port
      );
    
    /**
      Writes an 8-bit I/O port.
    
      Writes the 8-bit I/O port specified by Port with the value specified by Value
      and returns Value. This function must guarantee that all I/O read and write
      operations are serialized.
    
      If 8-bit I/O port operations are not supported, then ASSERT().
    
      @param  Port  The I/O port to write.
      @param  Value The value to write to the I/O port.
    
      @return The value written the I/O port.
    
    **/
    UINT8
    EFIAPI
    IoWrite8 (
      IN      UINTN                     Port,
      IN      UINT8                     Value
      );	
    

5.按照UEFI学习(二)将我们编写好的工程文件加入到MdeModulePkg的.dsc文件中并进行编译得到mySio.efi,将其转移到具备SHELL环境的u盘中,并在带有NCT5581D的测试机台上开机并进入shell环境,运行我们编译得到的mySio.efi文件。结果如下图所示,可以看到高八位的确如datasheet中描述的一样为default值D4h而低八位应该是因为版本更变。

在这里插入图片描述

目标一实现

至此,已经完成了我们的访问机制的验证。接下来我们可以开始去着手实现目标一了。对于参数的传入,我们可以通过创建shell工程应用去实现,通过学习《UEFI原理与编程》第三章的3.2.1,学习shell应用程序的特点与创建过程。接下来给大家分享一下个人对于目标一任务的实现并会在代码中作出一定解释帮助阅读者阅读。

SIO.c

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

/*
ShellCEntryLib.h和UefiBootServicesTableLib.h是shell应用工程需要,IoLib.h是IO读取需要,
ShellLib.h是调用ShellPrintEx所需要包含的库,用于指定坐标打印。
*/

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F
UINT8 symbol2=0;
UINT8 ROWNUM[16] = {0x00,0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0};

void StartToReadRC()//进入扩展模式
{
    IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);
    return;
}

void QuitRCReader()//推出扩展模式
{
    IoWrite8(IoIndexPort, 0xAA);
    return;
}

UINT8 SelectLogicalDevice(IN CHAR16* LDn)//选择逻辑设备并屏蔽部分保留的逻辑设备号,NCT5581d包含逻辑设备号0-9,A-F,10-16
{
    UINT8 symbol = 0;
    Print(L"The input LD is %s\n",LDn);
    if(LDn[0]!='L'||LDn[1]!='D')
    {
        ShellPrintEx(-1, -1, L"%HTHE LD parameter Input illegal!!\n");
        return symbol;
    }
    switch (LDn[2])
    {
        case '0':Print(L"This LD is reserved!\n"); break;
        case '1':if(LDn[3]=='6')//case 1的特殊是因为10-16的逻辑设备中只有16是有用的其余为保留
                    {IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x16); symbol = 1;}
                 else
                    {Print(L"This LD is reserved!\n"); }
                 break;
        case '2':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x02); symbol = 1;break;
        case '3':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x03); symbol = 1;break;
        case '4':Print(L"This LD is reserved!\n"); break;
        case '5':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x05); symbol = 1;break;
        case '6':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x06); symbol = 1;break;
        case '7':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x07); symbol = 1;break;
        case '8':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x08); symbol = 1;break;
        case '9':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x09); symbol = 1;break;
        case 'A':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0A); symbol = 1;break;
        case 'B':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0B); symbol = 1;break;
        case 'C':Print(L"This LD is reserved!\n"); break;
        case 'D':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0D); symbol = 1;break;
        case 'E':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0E); symbol = 1;break;
        case 'F':IoWrite8(IoIndexPort, 0x07); IoWrite8(IoDataPort, 0x0F); symbol = 1;break;
        default:ShellPrintEx(-1, -1, L"%HInput illegal!!\n");break;
    }
    return symbol;//symbol是为了判断是否成功
}

void ReadRC()//表头与内容打印的实现
{
    UINT8 RCvalue = 0,Times=0;
    INT32 colinit=0, rowinit=0;
    INT32 col=0, row=0,a=1,b=17;
    //Print(L"The row's value is %d!\n",gST->ConOut->Mode->CursorRow);
    Print(L"\n");
    colinit=gST->ConOut->Mode->CursorColumn;
    rowinit=gST->ConOut->Mode->CursorRow;

	//打印行表头
    ShellPrintEx(colinit, rowinit, L"%H     00  01  02  03  04  05  06  07  08  09  0A  0B  0C  0D  0E  0F");
    for (row =rowinit+a; row < rowinit+b; row++)
        {
            col = 0;
            ShellPrintEx(col, row,L"%H%02x", ROWNUM[row - 1-rowinit]);//打印列表头
            for (col = 5; col < 66;)
            {
                IoWrite8(IoIndexPort, Times);
                RCvalue= IoRead8(IoDataPort);
                ShellPrintEx(col, row, L"%B%02x",RCvalue);
                Times++;
                col += 4;
            }
        }
    if (Times==0)//unit8最大为255,而打印结束后Times已经增加到256即溢出为0了,而这一步是为了验证是否打印完全并正确
    {
        ShellPrintEx(0, rowinit+19, L"%HRCread is complete!\n");
    }
    else
    {
        ShellPrintEx(0,rowinit+ 19, L"%HRCread error!\n");
        Print(L"The Times's value is %d!\n",Times); 
    }
   return;
}



UINT8 Transfer(IN CHAR16 value)
{
    UINT8 number=0;

    if(value>47&&value<58)
        number=(UINT8)(value-48);
    else if(value>64&&value<71)
        number=(UINT8)(value-55);
    else if(value>96&&value<103)
        number=(UINT8)(value-87);
    else
        {Print(L"The format of the parameter value is incorrect!\n");symbol2=1;}
    return number;
}

INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{
    UINT8 offsetRT=0;
    UINT8 valueRT=0;
	//通过判断输入参数的个数来选择不同的功能
    switch (Argc)
    {
        case 1:Print(L"Argv[1] = NULL\n"); break;
        case 2:StartToReadRC();
               if(SelectLogicalDevice(Argv[1]))//?1
                ReadRC();
               else
                Print(L"Select Logical Device Fail!\n"); 
               QuitRCReader();
               break;
        case 3:Print(L"Missing Parameter!\n"); break;
        case 4:StartToReadRC();
               if(SelectLogicalDevice(Argv[1]))
                {
                    offsetRT=Transfer(Argv[2][0])*16+Transfer(Argv[2][1]);
                    Print(L"offsetRT is %x!\n",offsetRT); 
                    if(symbol2)
                        break;
                    else
                        {
                            
                            valueRT=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);
                            Print(L"valueRT is %x!\n",valueRT); 
                            if(symbol2)
                                break;
                            else
                                {IoWrite8(IoIndexPort,offsetRT );
                                IoWrite8(IoDataPort,valueRT );break;}
                        }
                }   
               QuitRCReader();
               break;
        default:Print(L"Not Ready Yet!\n"); 
            break;
    }


    return 0;
}

SIO.inf

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = SIO
  FILE_GUID                      = 7a6ca3b8-ee1b-489c-b300-24544a7bd418
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  SIO.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  ShellCEntryLib
  UefiLib
  IoLib
  ShellLib

结果演示

在这里插入图片描述

在这里插入图片描述

目标二实现

对于目标二的实现,首先需要对NCT5581D的GPIO具有一定的了解,掌握其配置所需要用到的寄存器。在datasheet的第17章中有对GPIO的详细介绍,The NCT5581D provides input/output ports that can be individually configured to perform a simple basic I/O function or alternative, pre-defined function. Users can configure each individual port to be an input or output port by programming respective bit in selection register (0 = output, 1 = input). Invert port value by setting inversion register (0 = non–inverse, 1 = inverse). Port value is read/write through data register.
而要实现具体的配置,以GP84为例,下面表中已经提示了要想实现GP84的GPIO功能需要先对其进行使能,配置相关的寄存器使GP84达到输入还是输出,极性等等。

在这里插入图片描述
在这里插入图片描述

  1. 首先是Logical Device 7 CR30h将GPIO8置active
    在这里插入图片描述

  2. 接下来需要注意的是在旧版的datasheet中可能很难查到的GPIO8使能位的设置,该设置位于全局变量寄存器CR2Ah中
    在这里插入图片描述

  3. 接下来要将GP84设置成需要的输入还是输出模式,电平极性是否需要反转,设置电平以及状态寄存器的读取以及功能复用的选择寄存器的设置寄存器位于 Logic Device 7, E4~E7 and ED
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 设置GPIO上拉还是开漏模式,寄存器位于 Logic Device F, CRE7
    在这里插入图片描述

了解了需要配置的寄存器,我们就可以开始实现目标二的要求了。因为是要使用万用表进行测量验证,所以在观察了原理图之后选择了GP84这个口,设想是将其配置GPIO输出模式,不用翻转极性,通过设置电平后用状态寄存器和万用表去验证功能,没有万用表时:在设置好相应寄存器后先让GPIO84清除状态寄存器,然后让GPIO84输出低电平再让GPIO84输出高电平,此时在状态寄存器的相应位可以读取到状态寄存器侦测到一个电平跳变,从而验证我们的设置是正确的。

下面列出我的GPIO.c:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F

INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{
	UINT8 GPIO84status=0;
	UINT8 GPIO84status2=0;
	//enter extern mode
	IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);

    //select logical device 07 to enable gpio84
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0x30);
    IoWrite8(IoDataPort, 0x04);
    GPIO84status2=(IoRead8(IoDataPort)>>2)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The cr30[2] is %x\n",GPIO84status2);

    //SET function to GPIO
    IoWrite8(IoIndexPort, 0x2A);
    GPIO84status2=IoRead8(IoDataPort)|0x80;
    IoWrite8(IoIndexPort, 0x2A);
    IoWrite8(IoDataPort, GPIO84status2);
    GPIO84status2=(IoRead8(IoDataPort)>>7)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The cr2A[8] is %x\n",GPIO84status2);

    // set gpio84 output
    IoWrite8(IoIndexPort, 0xE4);
    IoWrite8(IoDataPort, 0xEF);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The crE4[4] is %x\n",GPIO84status2);


    //set GPIO84 Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xEF);
    //set gpio84 output highlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x10);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status1 is %x\n",GPIO84status2);

    //read gpio8 status register to clear status
    IoWrite8(IoIndexPort, 0xE7);
    IoRead8(IoDataPort);

    //set GPIO84 OUT OF Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xFF);
    //set gpio84 output lowlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x00);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status2 is %x\n",GPIO84status2);

    //set GPIO84 Push-Pull
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x0F);
    IoWrite8(IoIndexPort, 0xE7);
    IoWrite8(IoDataPort, 0xEF);
    //set gpio84 output highlevel
    IoWrite8(IoIndexPort, 0x07);
    IoWrite8(IoDataPort, 0x07);    
    IoWrite8(IoIndexPort, 0xE5);
    IoWrite8(IoDataPort, 0x10);
    GPIO84status2=(IoRead8(IoDataPort)>>4)&0x01;
    ShellPrintEx(-1, -1, L"%B%H The GPIO84status3 is %x\n",GPIO84status2);


    IoWrite8(IoIndexPort, 0xE7);
    GPIO84status=IoRead8(IoDataPort);
    ShellPrintEx(-1, -1, L"%B%H The E7status is %x\n",GPIO84status2);
    GPIO84status=(GPIO84status>>4)&0x01;
    if(GPIO84status)
    	ShellPrintEx(-1, -1, L"%B%H GPIO Setting Success!");
    else
    	ShellPrintEx(-1, -1, L"%B%H GPIO Setting Fail!");

    IoWrite8(IoIndexPort, 0xAA);


	return 0;
}

可以看到,我的GPIO.c就是按照前面提到的配置去设置好相应的寄存器,就可以实现这个功能。

目标三实现

NCT5581D 的Hardware Monitor 监控PC硬件中的几个关键参数,包括电源电压、风扇速度和温度,所有这些对于高端计算机系统稳定和正常工作都是非常重要的。此外,专有的硬件减少了编程和处理器干预的数量,以控制冷却风扇的速度,最小化环境噪音,最大化系统可靠性。基于Hardware Monitor功能的强大和复杂性,单单前文提到的逻辑设备上的寄存器是远远不够的,所以NCT5581D为其提供了一个block去存放Hardware Monitor功能配置所需要的众多寄存器,那么问题就来了,对于这个block我们要怎么去访问呢?从datasheet的第八章我们可以看到,“NCT5581D提供了两个接口,LPC和I2C,供微处理器读取或写硬件监视器的内部寄存器。”而对于从LPC的访问方式是与前文提到的index/data对的IO访问方式是相似的,只不过前文使用的是2E/2F,而Hardware Monitor需要用到另外的端口号。在datasheet中同样提到了Hardware Monitor所用到的索引/数据对的基地址由使用超级I/O协议访问CR[60h]和CR[61h]获得。即想要获取Hardware Monitor需要用到的索引/数据对,我们先要选中Hardware Monitor对应的逻辑设备号,并读取其CR[60h]和CR[61h]的值,使用我们前面实现的打印功能,我们可以很容易的获取到这两个寄存器的值。分别为0x0A和0x20,但是需要注意的是,此时获取的仅仅只是基地址,而且基地址要由这两个寄存器怎么转换得到我们尚未可知,当我们继续往下看datasheet,在第九章的开头我们可以看到“地址端口和数据端口的基本地址在硬件监视设备逻辑设备B的寄存器CR60h和CR61h中指定。60h是高字节,61h是低字节。地址端口和数据端口分别位于基本地址,再加上5小时和6小时。例如,如果CR[60h]为02h,CR[61h]为90h,则地址端口为0x295h,数据端口为0x296h。”所以我们根据读到的CR[60h]和CR[61h]的值我们可以求得Hardware Monitor所用到的索引/数据对应该为A25/A26;至此,我们可以按此去访问Hardware Monitor的专属block了(通常把这种block称为IOspace)。

另外,第八章内容中还提到了“由于内部登记器的数量,有必要将登记器集划分为登记器4Eh指定的“bank”。”对此,我的个人理解是对前面选择逻辑设备号的一种套娃方式,把众多寄存器进行bank的分区,通过固定的寄存器去选择相应bank再去选择bank中的寄存器从而减少地址数量的需求。而对于选择bank则是在bank0的CR4E,该寄存器的描述为:

在这里插入图片描述

读到这里的时候,我当初会产生很多疑问,就是当我已经跳转到其他bank之后,我要怎么再选择bank0的4E去实现跳转到其他bank?难道每个bank的4e寄存器都可以实现跳转?那为什么datasheet要强调bank选择寄存器位于bank0的4E呢?而当我查阅了datasheet的第九章Hardware Monitor的众多寄存器后我发现,只有bank0具有4E寄存器,那么我的理解就是,在其他bank的4E寄存器的位置都是bank04E寄存器的映射,所以当我们跳转到其他bank后我们对该bank的4E进行写入便可实现对bank0的4E寄存器的值的修改,从而实现跳转。

掌握了对Hardware Monitor IOSpace的访问机制后,我们接下来就是要像配置GPIO功能一样去理解目标三中提到的功能实现的相关寄存器的配置。

CPU温度读取

对于CPU温度的读取需要理解PECI接口,从第八章内容我们可以理解相关的内容,“PECI(平台环境控制接口)是一种新的数字接口,可以读取Intel®CPU的CPU温度。PECI的带宽从2Kbps到2Mbps,它使用一根线进行自时钟和数据传输。通过与Intel®CPU中的数字热传感器(DTS)接口,PECI报告相对于激活热控制电路(TCC)的处理器温度的负温度(计数)。在TCC激活温度下,英特尔CPU将以降低性能运行,以防止设备发生热损坏。”
而读取的配置顺序为:

  1. BIOS have to set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    在这里插入图片描述

  2. Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    在这里插入图片描述

  3. Set Bank 7 Index 09h for Agent 0 Tbase
    在这里插入图片描述

  4. Set Bank 7 Index 01h = 95h to enable PECI3.0
    在这里插入图片描述

  5. Get temperature raw data from Bank 7 Index 17h (high byte) / 18h (low byte)
    在这里插入图片描述

  6. Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    在这里插入图片描述

  7. Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    在这里插入图片描述

  8. PECI Temperature Reading
    在这里插入图片描述

对于上述配置中的Tbase的配置和 raw data的配置可以从步骤8图中得到相应的关系,这两个步骤的意义在于当温度与我们自己测量到的温度有很大偏差时作调整用,而我们现在是为了练习对superio的访问,所以我们可以不对这两个步骤进行配置修改。以下是我的在CPU温度读取的代码实现:

UINTN ReadCPUTemperature()
{
    UINTN CPUTemperature=0;
    //set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    IoWrite8(IoIndexPort, 0x2C);
    IoWrite8(IoDataPort, 0x00);
    //Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7);
    IoWrite8(baseAddress+0x05, 0x03);
    IoWrite8(baseAddress+0x06, 0x10);
    //Set Bank 7 Index 01h = 95h to enable PECI3.0
    IoWrite8(baseAddress+0x05, 0x01);
    IoWrite8(baseAddress+0x06, 0x95);
    //Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 2); 
    IoWrite8(baseAddress+0x05, 0x00); 
    IoWrite8(baseAddress+0x06, 0x10);   
    //Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0); 
    IoWrite8(baseAddress+0x05, 0xAE); 
    IoWrite8(baseAddress+0x06, 0x01);    
    //Get temperature raw data from Bank 7 Index 20h 
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7); 
    IoWrite8(baseAddress+0x05, 0x20); 
    CPUTemperature= IoRead8(baseAddress+0x06);
    return CPUTemperature;
}

CPU电压读取

对于CPU电压的读取很简单,只需要读取相应的寄存器就能获得当前电压值,需要注意的是需要对读取到的数值进行转换才能得到正确值,即Detected Voltage = Reading * 0.008 V

在这里插入图片描述

UINTN ReadCPUVoltage()
{
    UINTN CPUVoltage=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0x80);
    CPUVoltage=IoRead8(baseAddress+0x06);
    return CPUVoltage*8;//此处不作除法运算是会丢失精度小数的实现在下面给出
}

//实现小数显示:将整数和小数部分分开显示,加上标点符号
void Specialtreatment(UINTN CPUVoltage)
{
    UINTN IntegerPart=0,FractionalPart=0;
    IntegerPart=CPUVoltage/1000;
    if (IntegerPart<10)
        ShellPrintEx(18,1,L"%d",IntegerPart);
    else if (IntegerPart<100)
        {
            ShellPrintEx(17,1,L"%d",IntegerPart);
            ShellPrintEx(25,1,L"This is impossible!!!!");
        }
    FractionalPart=CPUVoltage%1000; 
    ShellPrintEx(20,1,L"%03d",FractionalPart);
    return;
}

CPU风扇转速读取

对于CPU风扇转速的读取同样是简单的,而NCT5581D提供了两种方式去读取转速值
在这里插入图片描述

我采用的是第一种方式:

UINTN ReadCPUFanRPM()
{
    UINTN CPUFanRPM=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0xb2);
    CPUFanRPM=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0xb3);
    CPUFanRPM=CPUFanRPM*32+((IoRead8(baseAddress+0x06)<<3)>>3);//根据上图说明读取13bit的数值
    return 1350000/CPUFanRPM;
}

CPU风扇转速控制

CPU风扇转速控制如下图所示,其中输出模式上拉还是开漏以及pwm的输出频率因为尚未对此功能进行了解,所以我让它保持原来的值,不作修改。

在这里插入图片描述

而对应的真实输出的占空比关系如下图:

在这里插入图片描述

   //Select bank2 index02 bit[7:4]->00000010=02h
   IoWrite8(baseAddress+0x05, 0x4E);
   IoWrite8(baseAddress+0x06, 2);
   IoWrite8(baseAddress+0x05, 0x02);
   IoWrite8(baseAddress+0x06, 0x02); 
   //set PWM output(Duty)bank2 index09 bit[7:0] 00-FF
   fanSpeedDuty=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);//将从命令行获取的参数进行转换成目标占空比输入值
   ShellPrintEx(-1,-1,L"%H%2X",fanSpeedDuty);
   IoWrite8(baseAddress+0x05, 0x09);
   IoWrite8(baseAddress+0x06, fanSpeedDuty); 

目标四实现

WATCH DOG TIMER的工作原理很简单,当开启了WATCH DOG功能后向计数器寄存器写入相应值,计时器便会进行倒数,当数值没有被打断递减而减为0后,会向相应位置触发一个低电平跳变,当硬件连接将重启时序连入,便可实现机器的重启。
去配置一个WATCH DOG TIMER以WDT1为例需要进行以下配置:

在这里插入图片描述

个人对于目标三和四的实现

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseLib/BaseLibInternals.h>

#define  IoIndexPort 0x2E
#define  IoDataPort  0x2F

UINTN baseAddress=0;

void StartToReadRC()
{
    IoWrite8(IoIndexPort, 0x87);
    IoWrite8(IoIndexPort, 0x87);
    return;
}

UINTN ReadCPUTemperature()
{
    UINTN CPUTemperature=0;
    //set Global Control Register CR2C bit 0 = 0 to select Pin function as PECI.
    IoWrite8(IoIndexPort, 0x2C);
    IoWrite8(IoDataPort, 0x00);
    //Set Bank 7 Index 03h = 10h for Agent 0 and Domain 0
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7);
    IoWrite8(baseAddress+0x05, 0x03);
    IoWrite8(baseAddress+0x06, 0x10);
    //Set Bank 7 Index 01h = 95h to enable PECI3.0
    IoWrite8(baseAddress+0x05, 0x01);
    IoWrite8(baseAddress+0x06, 0x95);
    //Set Bank 2 Index 00h = 10h to select PECI Agent 0 as CPUFAN monitoring source
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 2); 
    IoWrite8(baseAddress+0x05, 0x00); 
    IoWrite8(baseAddress+0x06, 0x10);   
    //Set Bank 0 Index AEh bit 0 to 1 to enable PECI Agent 0 mode
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0); 
    IoWrite8(baseAddress+0x05, 0xAE); 
    IoWrite8(baseAddress+0x06, 0x01);    
    //Get temperature raw data from Bank 7 Index 20h 
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 7); 
    IoWrite8(baseAddress+0x05, 0x20); 
    CPUTemperature= IoRead8(baseAddress+0x06);
    return CPUTemperature;


}

UINTN ReadCPUVoltage()
{
    UINTN CPUVoltage=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0x80);
    CPUVoltage=IoRead8(baseAddress+0x06);
    return CPUVoltage*8;
}

UINTN ReadCPUFanRPM()
{
    UINTN CPUFanRPM=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 4);
    IoWrite8(baseAddress+0x05, 0xb2);
    CPUFanRPM=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0xb3);
    CPUFanRPM=CPUFanRPM*32+((IoRead8(baseAddress+0x06)<<3)>>3);
    return 1350000/CPUFanRPM;
}


UINTN ReadSYSTemperature()
{
    UINTN SYSTemperature=0;
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 0);
    IoWrite8(baseAddress+0x05, 0xAE);
    if(IoRead8(baseAddress+0x06)!=0)
        IoWrite8(baseAddress+0x06, 0);
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 6);
    IoWrite8(baseAddress+0x05, 0x22);
    IoWrite8(baseAddress+0x06, 0x02);
    IoWrite8(baseAddress+0x05, 0x4E);
    IoWrite8(baseAddress+0x06, 1);
    IoWrite8(baseAddress+0x05, 0x50);
    SYSTemperature=IoRead8(baseAddress+0x06);
    IoWrite8(baseAddress+0x05, 0x51);
    SYSTemperature=SYSTemperature*2+(IoRead8(baseAddress+0x06)>>7);
    SYSTemperature=SYSTemperature/2;
    return SYSTemperature;
}

void Specialtreatment(UINTN CPUVoltage)
{
    UINTN IntegerPart=0,FractionalPart=0;
    IntegerPart=CPUVoltage/1000;
    if (IntegerPart<10)
        ShellPrintEx(18,1,L"%d",IntegerPart);
    else if (IntegerPart<100)
        {
            ShellPrintEx(17,1,L"%d",IntegerPart);
            ShellPrintEx(25,1,L"This is impossible!!!!");
        }
    FractionalPart=CPUVoltage%1000; 
    ShellPrintEx(20,1,L"%03d",FractionalPart);
    return;
}

UINT8 Transfer(IN CHAR16 value)
{
    UINT8 number=0;

    if(value>47&&value<58)
        number=(UINT8)(value-48);
    else if(value>64&&value<71)
        number=(UINT8)(value-55);
    else if(value>96&&value<103)
        number=(UINT8)(value-87);
    else
        Print(L"The format of the parameter value is incorrect!\n");
    return number;
}

UINT8 isNumber(IN CHAR16 value)
{

    if(value>47&&value<58)
        return 1;
    else
        return 0;
}

// void GPIOinit()
// {
//     //select logical device 09 to enable gpio24
//     IoWrite8(IoIndexPort, 0x07);
//     IoWrite8(IoDataPort, 0x09);
//     IoWrite8(IoIndexPort, 0x30);
//     IoWrite8(IoDataPort, 0x01);

//     //SET function to GPIO
//     IoWrite8(IoIndexPort, 0x1B);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&EF);
//     IoWrite8(IoIndexPort, 0x27);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&F7);


//     // set gpio24 output
//     IoWrite8(IoIndexPort, 0x07);
//     IoWrite8(IoDataPort, 0x09);
//     IoWrite8(IoIndexPort, 0xE0);
//     IoWrite8(IoDataPort, IoRead8(IoDataPort)&EF);

//     return ;
// }

//同样通过检测命令行参数个数分为不同的功能
INTN
EFIAPI
ShellAppMain(
    IN UINTN Argc,
    IN CHAR16** Argv
)
{   
    UINTN counter = 0;
    UINTN CPUTemperature=0;
    UINTN CPUVoltage=0;
    UINTN CPUFanRPM=0;
    UINTN SYSTemperature=0;
    UINT8 fanSpeedDuty=0;
    UINT8 num=0;
    UINTN sec=0;
	if (Argc<=1)
    {
        Print(L"The input parament is not right!");
        return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='D')//D代表display即显示CPU温度、电压、风扇转速
    {
        //extern mode 
        StartToReadRC();
        //read logical device B 60h and 61h
        IoWrite8(IoIndexPort, 0x07);
        IoWrite8(IoDataPort, 0x0B);
        IoWrite8(IoIndexPort, 0x60);
        baseAddress=IoRead8(IoDataPort);
        IoWrite8(IoIndexPort, 0x61);
        baseAddress=baseAddress*256+IoRead8(IoDataPort);
        gST->ConOut->ClearScreen (gST->ConOut);
        ShellPrintEx(0,0,L"CPU Temperature:");//16
        ShellPrintEx(0,1,L"CPU Voltage:");
        ShellPrintEx(0,2,L"CPU Fan RPM:");
        ShellPrintEx(0,3,L"SYS Temperature:");
        ShellPrintEx(19,1,L".");

        while(counter<12580)//循环显示12580次
        {
            ShellPrintEx(18,0,L"      ");
            CPUTemperature=ReadCPUTemperature();
            ShellPrintEx(18,0,L"%d",CPUTemperature);
            ShellPrintEx(18,1,L" .    ");
            CPUVoltage=ReadCPUVoltage();
            Specialtreatment(CPUVoltage);
            ShellPrintEx(18,2,L"      ");
            CPUFanRPM=ReadCPUFanRPM();
            ShellPrintEx(18,2,L"%d",CPUFanRPM);
            ShellPrintEx(18,3,L"      ");
            SYSTemperature=ReadSYSTemperature();
            ShellPrintEx(18,3,L"%d",SYSTemperature);
            counter++;
        }
        gST->ConOut->ClearScreen (gST->ConOut);

        return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='C')//C是风扇转速控制
    {
        //extern mode 
        StartToReadRC();
        //read logical device B 60h and 61h
        IoWrite8(IoIndexPort, 0x07);
        IoWrite8(IoDataPort, 0x0B);
        IoWrite8(IoIndexPort, 0x60);
        baseAddress=IoRead8(IoDataPort);
        IoWrite8(IoIndexPort, 0x61);
        baseAddress=baseAddress*256+IoRead8(IoDataPort);
        //configuration registers CR24 bit3(keep)
        //configuration registers outputfrequency bank0,index02(keep)
        //Fan Control Mode Select bank2 index02 bit[7:4]
        if(Argc<4||(Argv[2][0]!='M'&& Argv[2][0]!='S'&& Argv[2][0]!='W'))
            {
                Print(L"The input parament to select fan mode and control fan speed is not right!");
                return EFI_SUCCESS;
            }
        else if(Argv[2][0]=='M')
            {
                //Select bank2 index02 bit[7:4]->00000010=02h
                IoWrite8(baseAddress+0x05, 0x4E);
                IoWrite8(baseAddress+0x06, 2);
                IoWrite8(baseAddress+0x05, 0x02);
                IoWrite8(baseAddress+0x06, 0x02); 
                //set PWM output(Duty)bank2 index09 bit[7:0] 00-FF
                fanSpeedDuty=Transfer(Argv[3][0])*16+Transfer(Argv[3][1]);
                ShellPrintEx(-1,-1,L"%H%2X",fanSpeedDuty);
                IoWrite8(baseAddress+0x05, 0x09);
                IoWrite8(baseAddress+0x06, fanSpeedDuty); 
                return EFI_SUCCESS;
            }
        else if(Argv[2][0]=='S')//这里是后面实现SmartFan需要
        {
            //Trying
            return EFI_SUCCESS;
        }
        else      
            return EFI_SUCCESS;
    }
    else if(Argv[1][0]=='W')//W代表看门狗实现
        {
            //
            while(isNumber(Argv[2][num]))
            {
                sec=sec*10+(Argv[2][num]-48);
                num++;
            }
            ShellPrintEx(-1,-1,L"setting second is :%H%d\n",sec);
            if(sec>255)
            {
                ShellPrintEx(-1,-1,L"setting second :%H%d is too big!Change the unit to make it!\n",sec);
                return EFI_SUCCESS;
            }
            StartToReadRC();
            IoWrite8(IoIndexPort, 0x07);
            IoWrite8(IoDataPort, 0x0D);
            IoWrite8(IoIndexPort, 0xF0);
            IoWrite8(IoDataPort, 0x00);

            IoWrite8(IoIndexPort, 0x07);
            IoWrite8(IoDataPort, 0x08);

            //active WDT1
            IoWrite8(IoIndexPort, 0x30);
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xEF)|0X01);
            //set second mode 
            IoWrite8(IoIndexPort, 0xF5);
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xF7)|0x02);
            IoWrite8(IoIndexPort, 0xF8);//WDT3
            IoWrite8(IoDataPort,(IoRead8(IoDataPort)&0xF6)|0x02);
            //set count down value
            IoWrite8(IoIndexPort, 0xF6);
            IoWrite8(IoDataPort, (UINT8)sec);
            IoWrite8(IoIndexPort, 0xF9);//WDT3
            IoWrite8(IoDataPort, (UINT8)sec);
            //read count down value
            gST->ConOut->ClearScreen (gST->ConOut);
            ShellPrintEx(0,0,L"WDT1 is counting down:   s");
           // ShellPrintEx(0,1,L"WDT3 is counting down:   s");
            
            while(1)
            {
                ShellPrintEx(21,0,L"   ");
                IoWrite8(IoIndexPort, 0xF6);
                ShellPrintEx(21,0,L"%H%d",IoRead8(IoDataPort));
                // ShellPrintEx(21,1,L"   ");
                // IoWrite8(IoIndexPort, 0xF9);
                // ShellPrintEx(21,1,L"%H%d",IoRead8(IoDataPort));
            }
            

            return EFI_SUCCESS;
        }
    else
        {
            Print(L"The input parament is not right!");
            return EFI_SUCCESS;
        }
}

至此,对NCT5581D的读取写入操作有了一定的了解,也是对edk2的一些应用的了解。
路漫漫其修远兮吾将上下而求索!

  • 21
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: UEFI BIOS Utility-EZ Mode是一种简单易用的BIOS设置界面,它提供了基本的系统信息、启动选项、调整CPU和内存频率、设置风扇控制等功能。它的设计使得用户可以轻松地进行基本的BIOS设置,而不需要深入了解BIOS的复杂性。 ### 回答2: UEFI BIOS Utility - EZ Mode是一种UEFI BIOS配置工具,它旨在使用户可以轻松管理和调整基本系统设置。EZ Mode提供了一个简化的图形用户界面面板,使用户能够快速而准确地调整各种BIOS选项。 EZ Mode界面以图形化方式呈现各种设置和参数,这使得用户可以更轻松地了解它们的含义和作用。例如,在EZ Mode中,用户可以快速地查看CPU速度、内存用量、系统温度、风扇转速等参数,并进行必要的调整以改善系统的性能和稳定性。 除了常规的设置,EZ Mode还提供了一些高级功能。例如,用户可以选择开启或关闭诸如Intel快速启动技术、USB 3.0支持、SATA控制器等功能,以最大程度地优化系统性能。 此外,EZ Mode还支持自动检测并更新BIOS固件,这可以保证系统始终运行在最新的稳定版本中。 总之,UEFI BIOS Utility - EZ Mode 是一个非常有用和功能强大的BIOS配置工具,它使用户在处理系统设置和参数时更加轻松。无论是普通用户还是高级用户,都可以从EZ Mode的功能中受益。 ### 回答3: UEFI BIOS Utility是现代计算机使用的一种新型Firmware。UEFI位于操作系统和硬件之间,主要负责计算机启动过程中的硬件自检以及操作系统的加载过程。UEFI BIOS Utility的EZ模式是一种简化操作的模式,旨在提供用户一个更加直观的界面,以便用户轻松地对计算机进行配置。 UEFI BIOS Utility的EZ模式提供了一个清晰的界面,可以让用户更加直观地了解计算机的硬件配置,系统信息以及启动选项等。在这种模式下,用户可以很方便地进行一些常见的设置,例如修改系统时间,设置启动顺序等等。同时,EZ模式还提供了导航面板,让用户轻松地找到需要的选项。 一些重要的系统配置也可以在EZ模式下进行调整。例如,用户可以配置CPU的频率和电压,设置内存检测参数等等。此外,用户还可以对硬盘进行分区,修改RAID设置和SATA模式等。 总之,UEFI BIOS Utility的EZ模式提供了一个真正的易用界面,让用户轻松处理大量的系统配置和硬件设置。当您需要进行系统调整的时候,把您的注意力放在EZ模式中,就不需要担心在BIOS中迷路了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值