UEFI之VFR

概述

UEFI用户交互界面的实现涉及到多种不同类型的文件,这里要讲的是VFR(Visual Forms Representation)文件,相比UNI文件它要复杂得多,理解起来也更困难。
UEFI交互界面那些字符串是来自UNI文件的(其实并不是全部来自UNI,也有部分是直接通过代码生成的),而整个窗体的框架部分则是来自VFR文件的。
在UEFI中,构成这样的窗体的组件大致有四种,分别是Strings,Forms,Fonts和Images,如下图所示:
在这里插入图片描述

Strings就是UNI文件提供的,Forms就是本文的VFR文件提供的,本文主要介绍的就是这个Forms,以及构成Forms的VFR文件。

Forms:描述了窗体的组织形式,提供了用户交互的方式和交互内容的存储方式等。
Forms是以二进制的形式提供的,这种二进制在EDK框架中被称为IFR(就是上述定义中提到的Internal Forms Representation)。
而IFR通过编译VFR来生成(关于编译工具,在EDK源代码中也有相应的源码,不过没有研究过不确定怎么用)。
因此,总的来说就是,我们通过编写VFR文件来完成对UEFI交互界面的组织形式和交互方式等相关内容的定义。

语法

VFR文件采用的是BNF语法,与DSC文件采用“#”作为注释标志不同,它使用“//”作为注释标志。另外还有两个关键字是“#define”和“#include”,用来定义和包含头文件的,类似于C的语法。

formset是VFR文件中最重要的部分,用来组成窗体的最常用的结构。其示例如下:
MdeModulePkg/Universal/DriverSampleDxe/Vfr.vfr

formset
guid = {0xcc5ebb4f, 0xf562, 0x11e7, {0x92, 0x11, 0xf4, 0x8c, 0x50, 0x49, 0xe3, 0xa4}},
title = STRING_TOKEN(STR_SAMPLE_FORM_SET_TITLE),
help = STRING_TOKEN(STR_SAMPLE_FORM_SET_HELP),
classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID
form formid = 1, title = STRING_TOKEN(STR_SAMPLE_FORM1_TITLE);
…
endform;
endformset;

formset是关键字,用来标志整个窗体的开始,与标志窗体结束的endformset成对出现。其中各关键字的含义为:
guid: 标志本formset的GUID值;
title:在界面中标志本formset的字符串标题;
help:在界面上显示本formset的帮助信息;
classguid: 本formaset所挂载页面的GUID值。

在formaset的集合中,可以用“form”和“endform”定义一个完整的页面,比如这篇博客中给出的示例

form formid = ADVANCED_MANAGER_FORM_ID,
    title  = STRING_TOKEN(STR_EDKII_MENU_TITLE);
    subtitle text = STRING_TOKEN(STR_DEVICES_LIST);

    label LABEL_DEVICES_LIST;
    label LABEL_END;

    subtitle text = STRING_TOKEN(STR_EMPTY_STRING);
    subtitle text = STRING_TOKEN(STR_EMPTY_STRING);
    subtitle text = STRING_TOKEN(STR_EXIT_STRING);
  endform;

FormSet 定义

vfrFormSetDefinition	::=
		"formset"
		"guid"	"="	guidDefinition	","
		"title"	"="	getStringId	","
		"help"	"="	getStringId	","
		{	"classguid"	"="	classguidDefinition	","	}
		{	"class"	"="	classDefinition	","	}
		{	"subclass"	"="	subclassDefinition	","	}
		vfrFormSetList
		"endformset"	";"
classguidDefinition	::=
		guidDefinition	{	"|"	guidDefinition	}	{	"|"	guidDefinition	}
classDefinition	::=
		validClassNames	(	"|"	validClassNames	)*
validClassNames	::=
				"NON_DEVICE"
		|	"DISK_DEVICE"
		|	"VIDEO_DEVICE"
		|	"NETWORK_DEVICE"
		|	"INPUT_DEVICE"
		|	"ONBOARD_DEVICE"
		|	"OTHER_DEVICE"
		|	Number
subclassDefinition	::=
				"SETUP_APPLICATION"
		|	"GENERAL_APPLICATION"
		|	"FRONT_PAGE"
		|	"SINGLE_USE"
		|	Number

例如

FormSet List

formset内部定义了很多的子选项,称为formset list,也就是上一节formset定义中的vfrFormSetList。
前面的例子中用到的form就是其中的一种

formset list可以有如下的内容:

vfrFormSetList	::=
		(
						vfrFormDefinition
				|	vfrFormMapDefinition
				|	vfrStatementImage
				|	vfrStatementVarStoreLinear
				|	vfrStatementVarStoreEfi
				|	vfrStatementVarStoreNameValue
				|	vfrStatementDefaultStore
				|	vfrStatementDisableIfFormSet
				|	vfrStatementSuppressIfFormSet
				|	vfrStatementExtension
				)*

上述的内容可以分为几种不同的类型:

  1. 变量定义,如defaultstore,varstore,efivarstore,namevaluevarstore等;
  2. 控制语句,它会做if判断来确定其包含的formset list是否会被使用,主要有disableif,suppressif,grayoutif和goto语句等(上述的语句只在目前只在form类型语句中见到过,在其外没遇见过,不确定是否可以在它之外);
  3. form语句,它们是formset里面的主体部分,有form,formmap等;

变量定义

  • Default Stores Definition
    defaultstore:
vfrStatementDefaultStore	::=
		"defaultstore"	StringIdentifier	","
		"prompt"	"="	getStringId
		{	","	"attribute"	"="	Number	}	";"

例:


  defaultstore MyStandardDefault,
    prompt      = STRING_TOKEN(STR_STANDARD_DEFAULT_PROMPT),
    attribute   = 0x0000;                         // Default ID: 0000 standard default

  defaultstore MyManufactureDefault,
    prompt      = STRING_TOKEN(STR_MANUFACTURE_DEFAULT_PROMPT),
    attribute   = 0x0001;                         // Default ID: 0001 manufacture
  • Variable Store Definition
    varstore
vfrStatementVarStoreLinear	::=
		"varstore"
		(
						StringIdentifier	","
				|	"UINT8"	","
				|	"UINT16"	","
				|	"UINT32"	","
				|	"UINT64"	","
				|	"EFI_HII_DATE"	","
				|	"EFI_HII_TIME"	","
				|	"EFI_HII_REF"	","
		)
		{	"varid"	"="	Number	","	}
		"name"	"="	StringIdentifier	","
		"guid"	"="	guidDefinition	";"

第一个StringIdentifier表示的是类型,第二个表示的是变量名,name和guid连起来就可以表示该特定的变量。

例:

 //
  // Define a Buffer Storage (EFI_IFR_VARSTORE)
  //
  varstore DRIVER_SAMPLE_CONFIGURATION,     // This is the data structure type
    varid = CONFIGURATION_VARSTORE_ID,      // Optional VarStore ID
    name  = MyIfrNVData,                    // Define referenced name in vfr
    guid  = IPMI_CONFIG_FORMSET_GUID;     // GUID of this buffer storage

它定义的是一个数据结构体变量,类型就是DRIVER_SAMPLE_CONFIGURATION。
MyIfrNVData是变量的名称,后面的VFR表达式中会通过该名称去引用该变量。
结构体原型在这里:DriverSampleDxe/NVDataStruc.h

  • EFI Variable Store Definition

efivarstore

vfrStatementVarStoreEfi	::=
		"efivarstore"
		(
						StringIdentifier	","
				|	"UINT8"	","
				|	"UINT16"	","
				|	"UINT32"	","
				|	"UINT64"	","
				|	"EFI_HII_DATE"	","
				|	"EFI_HII_TIME"	","
				|	"EFI_HII_REF"	","
		)
		{	"varid"	"="	Number	","	}
		"attribute"	"="	Number	(	"|"	Number	)*	","
		"name"	"="	StringIdentifier	","
		"guid"	"="	guidDefinition	";"

例:

 //
  // Define a EFI variable Storage (EFI_IFR_VARSTORE_EFI)
  //
  efivarstore MY_EFI_VARSTORE_DATA,
    attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,  // EFI variable attribures  
    name  = MyEfiVar,
    guid  = IPMI_CONFIG_FORMSET_GUID;

这里定义的就是UEFI变量,还可以声明变量的属性。,MY_EFI_VARSTORE_DATA 是结构体名
结构体原型在这里:DriverSampleDxe/NVDataStruc.h

  • Variable Name Store Definition

namevaluevarstore

vfrStatementVarStoreNameValue	::=
		"namevaluevarstore"	StringIdentifier	","
		{	"varid"	"="	Number	","	}
		(	"name"	"="	getStringId	","	)+
		"guid"	"="	guidDefinition	";

例:

 //
  // Define a Name/Value Storage (EFI_IFR_VARSTORE_NAME_VALUE)
  //
  namevaluevarstore MyNameValueVar,                // Define storage reference name in vfr
    name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME0), // Define Name list of this storage, refer it by MyNameValueVar[0]
    name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME1), // Define Name list of this storage, refer it by MyNameValueVar[1]
    name = STRING_TOKEN(STR_NAME_VALUE_VAR_NAME2), // Define Name list of this storage, refer it by MyNameValueVar[2]
    guid = IPMI_CONFIG_FORMSET_GUID;             // GUID of this Name/Value storage

控制语句

VFR文件中可以包含如下的控制语句

  • FormSet DisableIf Definition
    DisableIf语句,定义如下:
vfrStatementDisableIfFormSet	::=
	"disableif"	vfrStatementExpression	";"
			vfrFormSetList
	"endif"	";"

例:

disableif ideqval MyIfrNVData.SuppressGrayOutSomething == 0x2;
      orderedlist
        varid       = MyIfrNVData.OrderedList,
        prompt      = STRING_TOKEN(STR_TEST_OPCODE),
        help        = STRING_TOKEN(STR_TEXT_HELP),
        flags       = RESET_REQUIRED,
        option text = STRING_TOKEN(STR_ONE_OF_TEXT1), value = 3, flags = 0;
        option text = STRING_TOKEN(STR_ONE_OF_TEXT2), value = 2, flags = 0;
        option text = STRING_TOKEN(STR_ONE_OF_TEXT3), value = 1, flags = 0;
        default     = {1,2,3},
      endlist;
  endif;
  • FormSet SuppressIf Definition

SuppressIf语句,定义如下:

vfrStatementSuppressIfFormSet	::=
		"suppressif"	vfrStatementExpression	";"
				vfrFormSetList
		"endif"	";"

例:

 suppressif ideqval MyIfrNVData.BootOrderLarge == 0;
    form formid = 2,               // SecondSetupPage,
      title = STRING_TOKEN(STR_FORM2_TITLE);  // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code
……
endif
  • Statement GrayOutIf Definition

GrayOutIf语句,定义如下:

vfrStatementGrayOutIfStat	::=
		"grayoutif"	vfrStatementExpression	";"
		(	vfrStatementStatList	)*
		"endif"	";"

例如:

grayoutif  match2 (stringref(STRING_TOKEN(STR_PATTERN)), stringref(STRING_TOKEN(STR_STRING)), PERL_GUID);
      numeric 
        varid   = MyIfrNVData.Match2,
        prompt  = STRING_TOKEN(STR_MATCH2_PROMPT),
        help    = STRING_TOKEN(STR_MATCH2_HELP),
        minimum = 0,
        maximum = 243,
      endnumeric;
    endif;

需要注意几点:

  1. if条件之后有一个分号;

  2. 最后有一个endif与之对应;

  • Goto Statement Definition

goto 语句

vfrStatementGoto	::=
		"goto"
		{
				(
						"devicePath"		"="	getStringId	","
						"formsetguid"	"="	guidDefinition	","
						"formid"			"="	Number	","
						"question"	"="	Number	","
				)
				|
				(
						"formsetguid"	"="	guidDefinition	","
						"formid"			"="	Number	","
						"question"	"="	Number	","
				)
				|
				(
						"formid"			"="	Number	","
						"question"	"="
						(
										StringIdentifier	","
								|	Number	","
						)
				)
	|
				(
						Number	","
				)
		}
		vfrQuestionHeader
		{	","	"flags"	"="	vfrGotoFlags	}
		{	","	"key"	"="	Number	}
		{	","	vfrStatementQuestionOptionList	}
		";"
vfrGotoFlags	::=
		gotoFlagsField	(	"|"	gotoFlagsField	)*
gotoFlagsField	::=
				Number
		|	questionheaderFlagsField

key 是使用的question ID

例:
生成没有key的 EFI_IFR_REF

goto	1,
		prompt	=	STRING_TOKEN(STR_GOTO_PROMPT),
		help			=	STRING_TOKEN(STR_GOTO_HELP);

生成有key的 EFI_IFR_REF

goto	1,
		prompt	=	STRING_TOKEN(STR_GOTO_PROMPT),
		help			=	STRING_TOKEN(STR_GOTO_HELP);
		key				=	0x1234;

生成 EFI_IFR_REF2

goto
		formid			=	1,
		question	=	QuesttionRef,
		prompt			=	STRING_TOKEN(STR_GOTO_PROMPT),
		help					=	STRING_TOKEN(STR_GOTO_HELP);

生成 EFI_IFR_REF3

goto
		formsetguid	=	FORMSET_GUID,
		formid						=	1,
		question				=	QuesttionRef,
		prompt						=	STRING_TOKEN(STR_GOTO_PROMPT),
		help								=	STRING_TOKEN(STR_GOTO_HELP);

生成 EFI_IFR_REF4

goto
		devicepath		=	STRING_TOKEN(STR_DEVICE_PATH),
		formsetguid	=	FORMSET_GUID,
		formid						=	1,
		question				=	QuesttionRef,
		prompt						=	STRING_TOKEN(STR_GOTO_PROMPT),
		help								=	STRING_TOKEN(STR_GOTO_HELP);

生成EFI_IFR_REF5 没有 varid

goto
		prompt	=	STRING_TOKEN(STR_GOTO_PROMPT),
		help			=	STRING_TOKEN(STR_GOTO_HELP);

生成EFI_IFR_REF5 有 varid

goto
		varid			=	MySTestData.mFieldRef,
		prompt		=	STRING_TOKEN(STR_GOTO_PROMPT),
		help				=	STRING_TOKEN	STR_GOTO_HELP);
		default	=	FID;QID;GuidValue;STRING_TOKEN(STR_DEVICE_PATH),
		;

生成FI_IFR_REF 有 option code

goto	1,
		prompt	=	STRING_TOKEN(STR_GOTO_PROMPT),
		help			=	STRING_TOKEN(STR_GOTO_HELP),
		refresh	interval	=	3
		;

需要注意

  • Vfr不支持枚举,所以包含的.h中不能有枚举
  • 不支持数组大小做算数运算;
  • 包含的.h内容都会放到vfr.i 中,所以,包含的头文件一定要单一;

本文主要参考自《edk-ii-vfr-specification.pdf》和《Tiano+Setup+Design+Guide.pdfTiano+Setup+Design+Guide.pdf》,详细可以查看源文档
也可以查看这里:EdkCompatibilityPkg/Sample/Tools/Source/VfrCompile/VfrCompile.g

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值