# 68. VFR 추가 입력 요소 Part 3: oneof & orderedlist

## `oneof` 요소

`oneof` 요소는 미리 정의된 세트에서 옵션을 선택할 수 있다.\
(<https://edk2-docs.gitbook.io/edk-ii-vfr-specification/2_vfr_description_in_bnf/211_vfr_form_definition#2.11.6.6.2-vfr-oneof-statement-definition>)

`Form.vfr`에 다음 코드를 추가해주자.

```
oneof
  varid = FormData.OneOfValue,
  prompt = STRING_TOKEN(ONEOF_PROMPT),
  help = STRING_TOKEN(ONEOF_HELP),
  option text = STRING_TOKEN(ONEOF_OPTION1), value = 0x00, flags = DEFAULT;
  option text = STRING_TOKEN(ONEOF_OPTION2), value = 0x33, flags = 0;
  option text = STRING_TOKEN(ONEOF_OPTION3), value = 0x55, flags = 0;
endoneof;
```

Strings.uni에 새 문자열 토근을 추가해주자.

```
#string ONEOF_PROMPT           #language en-US  "OneOf list prompt"
#string ONEOF_HELP             #language en-US  "OneOf list help"
#string ONEOF_OPTION1          #language en-US  "OneOf list option 1"
#string ONEOF_OPTION2          #language en-US  "OneOf list option 2"
#string ONEOF_OPTION3          #language en-US  "OneOf list option 3"
```

마지막으로 Data.h에 값을 넣어주자.

```
typedef struct {
  ...
  UINT8 OneOf;
} UEFI_VARIABLE_STRUCTURE;
```

이를 Build 후 실행 시 아래와 같은 결과가 나타난다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fjah6YByI0SD2zBZrRvPz%2Fimage.png?alt=media&#x26;token=7078fb05-d85e-4231-981f-ee071b60a2f1" alt=""><figcaption></figcaption></figure>

사용 가능한 옵션 중 하나를 선택 가능하다.<br>

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FUYjXfgqt8Im6ZETSk6JD%2Fimage.png?alt=media&#x26;token=c4697a23-5675-49af-845e-670a7c1608d3" alt=""><figcaption></figcaption></figure>

옵션 2를 선택하면 아래와 같이 뜬다. 옵션 2는 위에서 value 값을 0x33으로 설정했다. 그렇기 때문에 dmpstore 명령으로 해당 변수의 값을 확인하면 0x33이 있는 것 확인 가능하다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FZu1F3za5ODmnv1Di5iDb%2Fimage.png?alt=media&#x26;token=59a63371-b983-4b94-bfda-92feaf4c1329" alt=""><figcaption></figcaption></figure>

IFR 코드는 아래와 같다\
(`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements/DEBUG/Form.lst`):

```
    oneof
>000000B4: 05 91 0F 00 10 00 06 00 01 00 20 00 00 10 00 55 00
      varid = FormData.OneOfValue,
      prompt = STRING_TOKEN(0x000F),
      help = STRING_TOKEN(0x0010),
      option text = STRING_TOKEN(0x0011), value = 0x00, flags = DEFAULT;
>000000C5: 09 07 11 00 10 00 00
      option text = STRING_TOKEN(0x0012), value = 0x33, flags = 0;
>000000CC: 09 07 12 00 00 00 33
      option text = STRING_TOKEN(0x0013), value = 0x55, flags = 0;
>000000D3: 09 07 13 00 00 00 55
    endoneof;
>000000DA: 29 02
```

첫 번째 opcode는 아래와 같다.

```
EFI_IFR_ONE_OF

Summary:
Creates a select-one-of question.

Prototype:

#define EFI_IFR_ONE_OF_OP 0x05

typedef struct _EFI_IFR_ONE_OF {
 EFI_IFR_OP_HEADER Header;
 EFI_IFR_QUESTION_HEADER Question;
 UINT8 Flags;
 union {
 struct {
 UINT8 MinValue;
 UINT8 MaxValue;
 UINT8 Step;
 } u8;
 struct {
 UINT16 MinValue;
 UINT16 MaxValue;
 UINT16 Step;
 } u16;
 struct {
 UINT32 MinValue;
 UINT32 MaxValue;
 UINT32 Step;
 } u32;
 struct {
 UINT64 MinValue;
 UINT64 MaxValue;
 UINT64 Step;
 } u64;
 } data;
} EFI_IFR_ONE_OF;

Members:
Header 		The sequence that defines the type of opcode as well as the length of the opcode being defined.
		Header.OpCode = EFI_IFR_ONE_OF_OP.
Question 	The standard question header.
Flags 		Specifies flags related to the numeric question.
MinValue 	The minimum value to be accepted by the browser for this opcode.
		The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags
MaxValue 	The maximum value to be accepted by the browser for this opcode.
		The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags
Step 		Defines the amount to increment or decrement the value each time a user requests a value change.
		If the step value is 0, then the input mechanism for the numeric value is to be free-form
		and require the user to type in the actual value.
		The size of the data field may vary from 8 to 64 bits, depending on the size specified in Flags

Description:
This opcode creates a select-on-of object, where the user must select from one of the nested options.
This is identical to EFI_IFR_NUMERIC.
```

옵션 opcode에 대한 정의는 다음과 같다.

```
EFI_IFR_ONE_OF_OPTION

Summary:
Creates a pre-defined option for a question.

Prototype:

#define EFI_IFR_ONE_OF_OPTION_OP 0x09

typedef struct _EFI_IFR_ONE_OF_OPTION {
 EFI_IFR_OP_HEADER Header;
 EFI_STRING_ID Option;
 UINT8 Flags;
 UINT8 Type;
 EFI_IFR_TYPE_VALUE Value;
} EFI_IFR_ONE_OF_OPTION;

Members:
Header	 The sequence that defines the type of opcode as well as the length of the opcode being defined.
	 Header.OpCode = EFI_IFR_ONE_OF_OPTION_OP.
Option   The string token reference to the option description string for this particular opcode.
Flags    Specifies the flags associated with the current option (EFI_IFR_OPTION_x)
Type 	 Specifies the type of the option’s value (See EFI_IFR_TYPE)
Value 	 The union of all of the different possible values. The actual contents (and size)
	 of the field depends on Type.
```

설명에 따르면 `Value` 필드의 내용은 `Type` 필드의 값에 따라 달라진다. 다음은 해당 type 플래그의 주석이 있는 모든 가능한 value type이다.

```
typedef union {
 UINT8 u8;             // EFI_IFR_TYPE_NUM_SIZE_8
 UINT16 u16;           // EFI_IFR_TYPE_NUM_SIZE_16
 UINT32 u32;           // EFI_IFR_TYPE_NUM_SIZE_32
 UINT64 u64;           // EFI_IFR_TYPE_NUM_SIZE_64
 BOOLEAN b;            // EFI_IFR_TYPE_BOOLEAN
 EFI_HII_TIME time;    // EFI_IFR_TYPE_TIME
 EFI_HII_DATE date;    // EFI_IFR_TYPE_DATE
 EFI_STRING_ID string; // EFI_IFR_TYPE_STRING, EFI_IFR_TYPE_ACTION
 EFI_HII_REF ref;      // EFI_IFR_TYPE_REF
 // UINT8 buffer[];    // EFI_IFR_TYPE_BUFFER
} EFI_IFR_TYPE_VALUE;
```

우리는 구조에서 데이터를 `UINT8 OneOf`로 선언했다. 따라서 컴파일러는 type 플래그를 `EFI_IFR_TYPE_NUM_SIZE_8`로 자동으로 공제했다. 이를 `UINT16 OneOf`로 변경하면 컴파일러는 type 필드 값을 `EFI_IFR_TYPE_NUM_SIZE_16`으로 변경한다.

아래의 출력을 이전의 출력과 비교해보자.

```
    oneof
>000000B4: 05 94 0F 00 10 00 06 00 01 00 20 00 00 11 00 00 55 00 00 00
      varid = FormData.OneOfValue,
      prompt = STRING_TOKEN(0x000F),
      help = STRING_TOKEN(0x0010),
      option text = STRING_TOKEN(0x0011), value = 0x00, flags = DEFAULT;
>000000C8: 09 08 11 00 11 01 00 00
      option text = STRING_TOKEN(0x0012), value = 0x33, flags = 0;
>000000D0: 09 08 12 00 01 01 33 00
      option text = STRING_TOKEN(0x0013), value = 0x55, flags = 0;
>000000D8: 09 08 13 00 01 01 55 00
    endoneof;
>000000E0: 29 02
```

예를 들어 type을 `EFI_HII_DATE OneOfValue`로 설정하려고 하면 `EFI_IFR_TYPE_VALUE`가 숫자외에 많은 값을 가질 수 있지만 오류가 발생한다.

```
ERROR 12288: OneOf question only support UINT8, UINT16, UINT32 and UINT64 data type
```

따라서 `EFI_IFR_TYPE_VALUE`의 하위 유형만 지원되는 것을 볼 수 있다.

## `orderedlist` 요소

옵션이 있는 또 다른 요소는 orderdlist 요소이다.\
(<https://edk2-docs.gitbook.io/edk-ii-vfr-specification/2_vfr_description_in_bnf/211_vfr_form_definition#2.11.6.8-vfr-orderedlist-statement-definition>)

이전과 동일하게 Form.vfr, Strings.uni, Data.h에 값을 넣어주겠다.\
(Data.h에는 3개의 옵션을 사용하기 위해서 배열을 3으로 선언했다.)

```
orderedlist
  varid = FormData.OrderedListValue,
  prompt = STRING_TOKEN(ORDERED_LIST_PROMPT),
  help = STRING_TOKEN(ORDERED_LIST_HELP),
  option text = STRING_TOKEN(ORDERED_LIST_OPTION1), value = 0x0A, flags = 0;
  option text = STRING_TOKEN(ORDERED_LIST_OPTION2), value = 0x0B, flags = 0;
  option text = STRING_TOKEN(ORDERED_LIST_OPTION3), value = 0x0C, flags = 0;
endlist;
```

```
#string ORDERED_LIST_PROMPT    #language en-US  "Ordered list prompt"
#string ORDERED_LIST_HELP      #language en-US  "Ordered list help"
#string ORDERED_LIST_OPTION1   #language en-US  "Ordered list option 1"
#string ORDERED_LIST_OPTION2   #language en-US  "Ordered list option 2"
#string ORDERED_LIST_OPTION3   #language en-US  "Ordered list option 3"
```

```
typedef struct {
  ...
  UINT8 OrderedListValue[3];
} UEFI_VARIABLE_STRUCTUR
```

빌드 후 실행하면 아래와 같은 화면이 나온다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FIzyg5ohaMhBX8vAaHaox%2Fimage.png?alt=media&#x26;token=53106cca-1e39-4dc6-a11d-8aa309389f09" alt=""><figcaption></figcaption></figure>

선택을 하면 아래와 같이 나오며 `+/-`를 통하여 이동이 가능하다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FT08kPLxT2iQVcOIvZELp%2Fimage.png?alt=media&#x26;token=1318a303-d949-4756-a48f-582757be6eae" alt=""><figcaption></figcaption></figure>

순서를 `2 1 3`으로 변경하고 `F10`을 눌러 저장해보자.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FIHyoN0HqaQA8hlqEXddP%2Fimage.png?alt=media&#x26;token=ff2ad6e7-fe4c-4a8d-9f5e-ffd06a9f6b8a" alt=""><figcaption></figcaption></figure>

그러면 UEFI 변수의 OrderedListValue 필드에 다음 데이터가 표시된다.

```
0B 0A 0C
```

## IFR

IFR코드를 살펴보자.\
(`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements/DEBUG/Form.lst`)

```
    orderedlist
>000000DC: 23 8F 14 00 15 00 07 00 01 00 21 00 00 03 00
      varid = FormData.OrderedListValue,
      prompt = STRING_TOKEN(0x0014),
      help = STRING_TOKEN(0x0015),
      option text = STRING_TOKEN(0x0016), value = 0x0A, flags = 0;
>000000EB: 09 07 16 00 00 00 0A
      option text = STRING_TOKEN(0x0017), value = 0x0B, flags = 0;
>000000F2: 09 07 17 00 00 00 0B
      option text = STRING_TOKEN(0x0018), value = 0x0C, flags = 0;
>000000F9: 09 07 18 00 00 00 0C
    endlist;
>00000100: 29 02
```

첫번째 코드 요소는 `EFI_IFR_ORDERED_LIST`이다.

```
EFI_IFR_ORDERED_LIST

Summary:
Creates a set question using an ordered list.

#define EFI_IFR_ORDERED_LIST_OP 0x23

typedef struct _EFI_IFR_ORDERED_LIST {
 EFI_IFR_OP_HEADER Header;
 EFI_IFR_QUESTION_HEADER Question;
 UINT8 MaxContainers;
 UINT8 Flags;
} EFI_IFR_ORDERED_LIST;

Members:
Header 		The byte sequence that defines the type of opcode as well as the length of the opcode being defined.
		Header.OpCode = EFI_IFR_ORDERED_LIST_OP.
Question 	The standard question header.
MaxContainers 	The maximum number of entries for which this tag will maintain an order.
		This value also identifies the size of the storage associated with this tag’s ordering array.
Flags		A bit-mask that determines which unique settings are active for this opcode.

Description:
Create an ordered list question in the current form. One thing to note is that valid values for the options
in ordered lists should never be a 0.
```

데이터를 필드와 일치시키면 새 필드에 대해 다음 데이터를 얻게 된다.

```
UINT8 MaxContainers;	// 0x03
UINT8 Flags;		// 0x00
```

모든 것이 order이고 요소에는 3개의 옵션이 있으므로 `MaxContainter=0x3`이다. 옵션 IFR 코드를 보면 옵션이 `oneof` 요소에서 사용된 것과 동일한 opcode `EFI_IFR_ONE_OF_OPTION`으로 인코딩된 것을 볼 수 있다.

원리는 동일하다. 데이터를 `UINT8`에서 `UINT16`으로 인코딩하는 경우 컴파일러는 옵션을`EFI_IFR_TYPE_NUM_SIZE_16`(=`UINT16`)으로 인코딩하고 스토리지에 다음과 같이 표시된다.

```
UINT16 OrderedListValue[3];
```

```
0B 00 0A 00 0C 00
```

`orderedlist`를 사용하면 숫자가 아닌 데이터 유형을 사용할 수도 있다. 예를 들어 변수를 날짜 배열로 인코딩도 가능하다.

```
EFI_HII_DATE OrderedListValue[3];
```

당연히 VFR에서 코드를 변경 해줘야 한다.

```
orderedlist
  varid = FormData.OrderedListValue,
  prompt = STRING_TOKEN(ORDERED_LIST_PROMPT),
  help = STRING_TOKEN(ORDERED_LIST_HELP),
  option text = STRING_TOKEN(ORDERED_LIST_OPTION1), value = 2021/7/4, flags = 0;
  option text = STRING_TOKEN(ORDERED_LIST_OPTION2), value = 2022/8/5, flags = 0;
  option text = STRING_TOKEN(ORDERED_LIST_OPTION3), value = 2023/9/6, flags = 0;
endlist;
```

이런 코드에 대한 IFR 데이터를 파싱하면 모든 옵션이 `#define EFI_IFR_TYPE_DATE 0x06` type으로 인코딩된 것을 볼 수 있다.

```
    orderedlist
>000000DC: 23 8F 14 00 15 00 07 00 01 00 21 00 00 0C 00
      varid = FormData.OrderedListValue,
      prompt = STRING_TOKEN(0x0014),
      help = STRING_TOKEN(0x0015),
      option text = STRING_TOKEN(0x0016), value = 2021/7/4, flags = 0;
>000000EB: 09 0A 16 00 06 06 E5 07 07 04
      option text = STRING_TOKEN(0x0017), value = 2022/8/5, flags = 0;
>000000F5: 09 0A 17 00 06 06 E6 07 08 05
      option text = STRING_TOKEN(0x0018), value = 2023/9/6, flags = 0;
>000000FF: 09 0A 18 00 06 06 E7 07 09 06
    endlist;
>00000109: 29 02
```

데이터 배열의 크기가 사용 가능한 옵션보다 작으면 모든 것이 컴파일되지만 HII에서는 첫번째 `array size` 옵션만 표시된다. 예를 들어 `UINT8 OrderedListValue[2]`의 결과는 아래와 같다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2FXKA1bdI5lfCYgEiQ6nub%2Fimage.png?alt=media&#x26;token=d30e64d4-3839-4124-97b2-6bdd0c23b0a7" alt=""><figcaption></figcaption></figure>
