UEFI学习——使用gRT->GetVariable读取Setup选项值

先列出代码,程序的解释在后面。

       代码:

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Universal\DriverSampleDxe\DriverSample.h>


#define EFI_IFR_REFRESH_ID_OP_GUID \
  { \
    0xF5E655D9, 0x02A6, 0x46f2, {0x9E, 0x76, 0xB8, 0xBE, 0x8E, 0x60, 0xAB, 0x22} \
  }
EFI_GUID      gDriverSampleFormSetGuid = DRIVER_SAMPLE_FORMSET_GUID;
CHAR16        VariableName[] = L"MyIfrNVData";
EFI_GUID      gEfiIfrRefreshIdOpGuid = EFI_IFR_REFRESH_ID_OP_GUID;
EFI_EVENT     mEvent;

VOID
EFIAPI
EfiEventGetVariable (
  IN EFI_EVENT              Event,
  IN VOID                   *Context
  )
{
    EFI_STATUS                            Status = EFI_SUCCESS;
    UINTN                                 BufferSize;
    DRIVER_SAMPLE_CONFIGURATION           DataBuffer;

    BufferSize = sizeof (DRIVER_SAMPLE_CONFIGURATION);

    Status = gRT->GetVariable(
        VariableName,
        &gDriverSampleFormSetGuid,
        NULL,
        &BufferSize,
        &DataBuffer);
    if(EFI_ERROR(Status)){ 
     DEBUG((DEBUG_INFO,"gRT->GetVariable Failed!-------- %r\n", Status));
   }
    DEBUG((EFI_D_ERROR,"SetupRead Invoke Sucess! The data is:%r\n",DataBuffer.SuppressGrayOutSomething));

    gBS->CloseEvent (mEvent);
}

EFI_STATUS EFIAPI SetupRead(
    IN EFI_HANDLE             ImageHandle,
    IN EFI_SYSTEM_TABLE       *SystemTable
)
{
    EFI_STATUS                            Status = EFI_SUCCESS;

    DEBUG((EFI_D_ERROR, "SetupRead Start! \n"));
    Status = gBS->CreateEventEx (
        EVT_NOTIFY_SIGNAL, 
        TPL_NOTIFY,
        EfiEventGetVariable,
        NULL,
        &gEfiIfrRefreshIdOpGuid,
        &mEvent
        );
    ASSERT_EFI_ERROR (Status);
    DEBUG((EFI_D_ERROR, "gBS->CreateEventEx Success! - %r\n",Status));

    return EFI_SUCCESS;
}

.inf

[Defines]
 INF_VERSION                 = 0x00010005
 BASE_NAME                   = SetupRead
 FILE_GUID                   = 395A072D-0660-4043-ADB6-09915F262B4B
 MODULE_TYPE                 = DXE_DRIVER
 VERSION_STRING              = 1.0
 ENTRY_POINT                 = SetupRead


[Sources.common]
 SetupRead.c

[Packages]

 Nt32Pkg/Nt32Pkg.dec
 MdePkg/MdePkg.dec
 MdeModulePkg/MdeModulePkg.dec
 IntelFrameworkPkg/IntelFrameworkPkg.dec
 
[LibraryClasses]
 UefiDriverEntryPoint
 UefiBootServicesTableLib
 DebugLib


[Depex]
 gEfiSimpleTextOutProtocolGuid

ps:PEIM和DXE Module在dsc和fdf里都要加上相应路径,只在dsc里加编译可以通过,但是不能正确查看运行结果。

1.要获取的Setup选项值

       学习获取Setup选项值之前建议先了解一下uni文件和vfr文件。

       我获取的是UEFI模拟环境中的选项值,它在模拟环境下的Setup界面中的Device Manager->Browser Testcase Engine->My one-of prompt #1,它的值有三个分别是Suppress the Checkbox、GrayOut the Checkbox、Enable Checkbox。

       那么怎样才能在EDKII源码里找到要获取的Setup选项所对应的变量呢?

       我用的代码编辑器是VSCode,首先在搜索里搜你要获取的Setup选项如My one-of prompt #1(在.uni文件里找),可以找到这一行#string STR_ONE_OF_PROMPT #language en-US "My one-of prompt #1"然后再搜索STR_ONE_OF_PROMPT(在.vfr文件里找),可以找到
在这里插入图片描述

       其中SuppressGrayOutSomething就是要找的选项值,再进一步搜索可知SuppressGrayOutSomething是结构体DRIVER_SAMPLE_CONFIGURATION的一个成员,这样就知道要找的Setup选项在EDKII源码里所对应的变量了。

       要使用gRT->GetVariable获取Setup选项值必须的两个值是变量名和Guid,这两个值是对应的,结构体DRIVER_SAMPLE_CONFIGURATION对应的变量名和Guid是L"MyIfrNVData"和DRIVER_SAMPLE_FORMSET_GUID。

2.gRT->GetVariable和gRT->SetVariable

       了解了要获取的Setup选项值就需要了解gRT->GetVariable和gRT->SetVariable,gRT->GetVariable用来获取Setup选项值,而在gRT->GetVariable之前必须要先调用gRT->SetVariable函数,这样GetVariable函数才能调用成功。

       UEFI系统变量服务包括读取变量的GetVariable、更新或创建变量的SetVariable,以及用于遍历系统变量的GetNextVariableName,这里只介绍GetVariable和SetVariable。
       gRT->GetVariable的函数原型:

/**
  Returns the value of a variable.

  @param[in]       VariableName  A Null-terminated string that is the name of the vendor's
                                 variable.
  @param[in]       VendorGuid    A unique identifier for the vendor.
  @param[out]      Attributes    If not NULL, a pointer to the memory location to return the
                                 attributes bitmask for the variable.
  @param[in, out]  DataSize      On input, the size in bytes of the return Data buffer.
                                 On output the size of data returned in Data.
  @param[out]      Data          The buffer to return the contents of the variable. May be NULL
                                 with a zero DataSize in order to determine the size buffer needed.

  @retval EFI_SUCCESS            成功返回
  @retval EFI_NOT_FOUND          变量不存在
  @retval EFI_BUFFER_TOO_SMALL   缓冲区大小DataSize太小
  @retval EFI_INVALID_PARAMETER  VariableName/VendorGuid/DataSize/Data为空

  @retval EFI_DEVICE_ERROR       设备返回错误
  @retval EFI_SECURITY_VIOLATION 该变量需要身份验证,但身份验证没通过

**/
typedef
EFI_STATUS
(EFIAPI *EFI_GET_VARIABLE)(
  IN     CHAR16                      *VariableName,  //变量名字
  IN     EFI_GUID                    *VendorGuid,    //变量所有者GUID
  OUT    UINT32                      *Attributes,    OPTIONAL //变量属性
  IN OUT UINTN                       *DataSize,     //缓冲区Data大小
  OUT    VOID                        *Data           OPTIONAL //返回变量的值
  );

       属性值Attributes是32位的无符号整数,每一位表示一种属性,其属性值定义如下:

#define EFI_VARIABLE_NON_VOLATILE                            0x00000001 //关机后仍然有效
#define EFI_VARIABLE_BOOTSERVICE_ACCESS                      0x00000002 //启动服务期间有效
#define EFI_VARIABLE_RUNTIME_ACCESS                          0x00000004 //Runtime期间有效
#define EFI_VARIABLE_HARDWARE_ERROR_RECORD                   0x00000008 //硬件错误记录
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS              0x00000010 //写时需要验证身份
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS   0x00000020 //已弃用,应考虑保留
#define EFI_VARIABLE_APPEND_WRITE                            0x00000040 //附加写

       例如,某变量的属性为0x00000011(即0x00000001|0x00000010),表示该变量关机后仍然有效,并且写该变量时需提供身份验证信息。所谓关机后仍然有效,意思是该变量要写入非易失性存储器中。若变量没有EFI_VARIABLE_NON_VOLATILE属性,则该变量仅保存在内存中。
       VendorGuid可以作为命名空间使用。不同的操作系统或第三方厂商可以管理属于自己的变量,使用VendorGuid可以避免命名冲突。最常用的VendorGuid是gEfiGlobalVariableGuid,它管理了全局的系统变量,如L"Lang"、L"BootOrder"、L"BootCurrent"等。这次使用的VendorGuid是gDriverSampleFormSetGuid(=DRIVER_SAMPLE_FORMSET_GUID),它管理了L"MyIfrNVData"。

       SetVariable有三项功能:新建、更新和删除变量。若变量不存在,则执行新建操作;若变量存在并且该变量允许写,则执行更新操作;删除一个变量(非AT变量)的两种方式:
       ①DataSize为0,并且Attributes中不包含下列属性中任意一个。

EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 
EFI_VARIABLE_APPEND_WRITE    
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS    

       ②Attributes设为0.

3.利用事件辅助获取Setup值

       为什么要用到事件呢?之前说过在gRT->GetVariable之前必须要调用gRT->SetVariable函数,但是代码中没有使用SetVariable函数。因为在EDKII源码中的DriverSimple.c文件已经使用了SetVariable函数,而且VariableName和VendorGuid与我们的一样,所以可以做个近水楼台,在我们的.c文件中只调用GetVariable。但是这就有一个问题,怎么保证DriverSimple.c文件中的SetVariable相比于我们的.c文件里的GetVariable要先执行呢?这里又有个近水楼台,在DriverSimple.c的SetVariable函数后面,有一个gBS->CreateEventEx(不了解的可以先去学习UEFI中的事件),我们可以在我们的.c文件中也创建一个gBS->CreateEventEx,将并且将这两个.c文件中的事件函数(即CreateEventEx的第三个参数)都挂在同一个Guid上。然后,在DriverSimple.c的gBS->CreateEventEx后面再加一句gBS->SignalEvent (mEvent),DriverSimple.c里触发事件,这样就可以使DriverSimple.c里的SetVariable先于我们的.c的GetVariable执行。然后就可以成功读取Setup选项值。
       这个过程可以概括为:
                                          在这里插入图片描述
关于UEFI是事件函数可以参考戴正华《UEFI原理与编程》第六章,或我的一篇博客:UEFI学习——事件函数WaitForEvent和CreateEvent/CreateEventEx

       有人可能感觉这个有点麻烦,在自己的.c里直接使用SetVariable不香吗,直接使用我还没试过,应该也可以,我这样做是为了加深对事件的理解和学习,未尝不好。

4.运行结果

在这里插入图片描述
       在Setup界面里查看:
在这里插入图片描述
       ps:
       在.uni文件里,对Suppress the Checkbox、GrayOut the Checkbox、Enable Checkbox的定义分别为:

#string STR_ONE_OF_TEXT4               #language en-US "Suppress the Checkbox"
                                       #language fr-FR "Mi uno- del texto # 4"
#string STR_ONE_OF_TEXT5               #language en-US "GrayOut the Checkbox"
                                       #language fr-FR "Mi uno- del texto # 5"
#string STR_ONE_OF_TEXT6               #language en-US "Enable Checkbox"

       在.vfr文件里,Suppress the Checkbox、GrayOut the Checkbox、Enable Checkbox的值分别为:
在这里插入图片描述
       所以代码运行结果正确。

static int sbsa_uart_probe(struct platform_device *pdev) { struct uart_amba_port *uap; struct resource r; int portnr, ret; int baudrate; / * Check the mandatory baud rate parameter in the DT node early * so that we can easily exit with the error. */ if (pdev->dev.of_node) { struct device_node *np = pdev->dev.of_node; ret = of_property_read_u32(np, "current-speed", &baudrate); if (ret) return ret; } else { baudrate = 115200; } portnr = pl011_find_free_port(); if (portnr < 0) return portnr; uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), GFP_KERNEL); if (!uap) return -ENOMEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } uap->port.irq = ret; #ifdef CONFIG_ACPI_SPCR_TABLE if (qdf2400_e44_present) { dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); uap->vendor = &vendor_qdt_qdf2400_e44; } else #endif uap->vendor = &vendor_sbsa; uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; snprintf(uap->type, sizeof(uap->type), "SBSA"); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ret = pl011_setup_port(&pdev->dev, uap, r, portnr); if (ret) return ret; platform_set_drvdata(pdev, uap); return pl011_register_port(uap); }在上述代码中,我需要添加一个功能:在以uefi方式启动系统时,uart驱动会读取acpi表内有关波特率的设置,并以这个进行串口波特率设置,请根据我的要求,在原代码中添加这一功能
06-07
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值