From c28cd7e7867f856318c121a15f73f2b499dd2bb9 Mon Sep 17 00:00:00 2001 From: Konstantin Aladyshev Date: Mon, 21 Mar 2022 17:18:32 +0300 Subject: Add lesson 66 Signed-off-by: Konstantin Aladyshev --- Lessons/Lesson_66/README.md | 137 ++++++++++++++++++++ Lessons/Lesson_66/String1.png | Bin 0 -> 4896 bytes Lessons/Lesson_66/String2.png | Bin 0 -> 6063 bytes Lessons/Lesson_66/String3.png | Bin 0 -> 5567 bytes Lessons/Lesson_66/String4.png | Bin 0 -> 5180 bytes .../UefiLessonsPkg/HIIFormDataElements/Data.h | 16 +++ .../UefiLessonsPkg/HIIFormDataElements/Form.vfr | 34 +++++ .../HIIFormDataElements/HIIFormDataElements.c | 140 +++++++++++++++++++++ .../HIIFormDataElements/HIIFormDataElements.inf | 22 ++++ .../UefiLessonsPkg/HIIFormDataElements/Strings.uni | 17 +++ README.md | 1 + 11 files changed, 367 insertions(+) create mode 100644 Lessons/Lesson_66/README.md create mode 100644 Lessons/Lesson_66/String1.png create mode 100644 Lessons/Lesson_66/String2.png create mode 100644 Lessons/Lesson_66/String3.png create mode 100644 Lessons/Lesson_66/String4.png create mode 100644 Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Data.h create mode 100644 Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Form.vfr create mode 100644 Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.c create mode 100644 Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.inf create mode 100644 Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Strings.uni diff --git a/Lessons/Lesson_66/README.md b/Lessons/Lesson_66/README.md new file mode 100644 index 0000000..b46ec49 --- /dev/null +++ b/Lessons/Lesson_66/README.md @@ -0,0 +1,137 @@ +Let's look at another data input VFR element - `string` (https://edk2-docs.gitbook.io/edk-ii-vfr-specification/2_vfr_description_in_bnf/211_vfr_form_definition#2.11.6.7.1-vfr-string-statement-definition) + +This element is intended to store `CHAR16` strings. + +Here is minimal code that we need to add to our VFR code. `minsize`/`maxsize` elements are mandatory for the `string` element: +``` +string + varid = FormData.StringValue, + prompt = STRING_TOKEN(STRING_PROMPT), + help = STRING_TOKEN(STRING_HELP), + minsize = 5, + maxsize = 10, +endstring; +``` + +Off course add new tokens to the `UefiLessonsPkg/HIIFormDataElements/Strings.uni`: +``` +#string STRING_PROMPT #language en-US "String prompt" +#string STRING_HELP #language en-US "String help" +``` + +As for our `UEFI_VARIABLE_STRUCTURE`, we need to add array of `UINT16` elements with a size at least of `maxsize`: +``` +typedef struct { + UINT8 CheckboxValue; + UINT16 NumericValue; + UINT16 StringValue[10]; +} UEFI_VARIABLE_STRUCTURE; +``` + +If array size is lower than `maxsize` EDKII build system would fail: +``` +ERROR 12288: String MaxSize can't be larger than the max number of elements in string array. +``` + +Don't forget to delete old UEFI Variable before loading our updated driver: +``` +Shell> dmpstore -guid 531bc507-9191-4fa2-9446-b844e35dd12a -d +Delete variable '531BC507-9191-4FA2-9446-B844E35DD12A:FormData': Success +Shell> load fs0:HIIFormDataElements.efi +``` + +By default element looks like this: +![String1](String1.png?raw=true "String1") + +As we have `minsize` string limit, it is not possible to enter string smaller than 5 characters: +![String2](String2.png?raw=true "String2") + +It is also not possible to enter string longer than 10 characters. HII simply won't allow you to type symbols beyond the limit: +![String3](String3.png?raw=true "String3") + +But if everything is in limits, you can successfully save data: +![String4](String4.png?raw=true "String4") + +After that you can check our UEFI variable storage: +``` +Shell> dmpstore -guid 531bc507-9191-4fa2-9446-b844e35dd12a +Variable NV+BS '531BC507-9191-4FA2-9446-B844E35DD12A:FormData' DataSize = 0x17 + 00000000: 00 00 00 55 00 45 00 46-00 49 00 2D 00 4C 00 65 *...U.E.F.I.-.L.e* + 00000010: 00 73 00 73 00 6F 00 *.s.s.o.* +``` + +As you can see the string doesn't contain `\0` terminating symbol. So for simplicity maybe it is better to declare array size one element larger than the value in the VFR `maxsize` field. + field (i.e. `CHAR16 StringValue[11]`). + +This way it would be easier to parse string field from our UEFI variable: +``` +Shell> dmpstore -guid 531bc507-9191-4fa2-9446-b844e35dd12a +Variable NV+BS '531BC507-9191-4FA2-9446-B844E35DD12A:FormData' DataSize = 0x19 + 00000000: 00 00 00 55 00 45 00 46-00 49 00 2D 00 4C 00 65 *...U.E.F.I.-.L.e* + 00000010: 00 73 00 73 00 6F 00 00-00 *.s.s.o...* +``` + +# MULTI_LINE + +VFR specification defines a `MULTI_LINE` flag for the `string` element: +``` +string + varid = FormData.StringValue, + prompt = STRING_TOKEN(STRING_PROMPT), + help = STRING_TOKEN(STRING_HELP), + flags = MULTI_LINE, + minsize = 5, + maxsize = 10, +endstring; +``` + +This flag should work as a hint for the form browser that multi-line text can be allowed. But currently OVMF form broswer doesn't use this flag. + +# IFR + +You can look at IFR code `Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements/DEBUG/Form.lst`: +``` + string +>00000082: 1C 90 09 00 0A 00 03 00 01 00 03 00 00 05 0A 00 + varid = FormData.StringValue, + prompt = STRING_TOKEN(0x0009), + help = STRING_TOKEN(0x000A), + minsize = 5, + maxsize = 10, + endstring; +>00000092: 29 02 +``` + +The data encodes `EFI_IFR_STRING` structure: +``` +Summary: +Defines the string question. + +Prototype: +#define EFI_IFR_STRING_OP 0x1C + +typedef struct _EFI_IFR_STRING { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 MinSize; + UINT8 MaxSize; + UINT8 Flags; +} EFI_IFR_STRING; + +Members: +Header The sequence that defines the type of opcode as well as the length of the opcode being defined. + Header.OpCode = EFI_IFR_STRING_OP. +Question The standard question header. +MinSize The minimum number of characters that can be accepted for this opcode. +MaxSize The maximum number of characters that can be accepted for this opcode. +Flags Flags which control the string editing behavior. + +Description: +This creates a string question. The minimum length is MinSize and the maximum length is MaxSize +``` + +You can parse the data, but really there is nothing new for us. + +# Note + +At the time of this writing you can cheat EDKII build system and declare `StringValue` array with the size of 10, but with a type of `UINT8`. The code would compile, but as you can guess, you shouldn't expect anything good from this. Right now because of this error HII system would allow you to "save" form and wouldn't even complain. But as an actual value data still wouldn't be equal to the form data, HII would you suggest to save form data endlessly. diff --git a/Lessons/Lesson_66/String1.png b/Lessons/Lesson_66/String1.png new file mode 100644 index 0000000..2c93747 Binary files /dev/null and b/Lessons/Lesson_66/String1.png differ diff --git a/Lessons/Lesson_66/String2.png b/Lessons/Lesson_66/String2.png new file mode 100644 index 0000000..9194993 Binary files /dev/null and b/Lessons/Lesson_66/String2.png differ diff --git a/Lessons/Lesson_66/String3.png b/Lessons/Lesson_66/String3.png new file mode 100644 index 0000000..04e160a Binary files /dev/null and b/Lessons/Lesson_66/String3.png differ diff --git a/Lessons/Lesson_66/String4.png b/Lessons/Lesson_66/String4.png new file mode 100644 index 0000000..2e8b7fd Binary files /dev/null and b/Lessons/Lesson_66/String4.png differ diff --git a/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Data.h b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Data.h new file mode 100644 index 0000000..80bd3dc --- /dev/null +++ b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Data.h @@ -0,0 +1,16 @@ +#ifndef _DATA_H_ +#define _DATA_H_ + +#define FORMSET_GUID {0x531bc507, 0x9191, 0x4fa2, {0x94, 0x46, 0xb8, 0x44, 0xe3, 0x5d, 0xd1, 0x2a}} + +#define UEFI_VARIABLE_STRUCTURE_NAME L"FormData" + +#pragma pack(1) +typedef struct { + UINT8 CheckboxValue; + UINT16 NumericValue; + CHAR16 StringValue[11]; +} UEFI_VARIABLE_STRUCTURE; +#pragma pack() + +#endif diff --git a/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Form.vfr b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Form.vfr new file mode 100644 index 0000000..42affd4 --- /dev/null +++ b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Form.vfr @@ -0,0 +1,34 @@ +#include +#include "Data.h" + +formset + guid = FORMSET_GUID, + title = STRING_TOKEN(FORMSET_TITLE), + help = STRING_TOKEN(FORMSET_HELP), + + efivarstore UEFI_VARIABLE_STRUCTURE, + attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + name = FormData, + guid = FORMSET_GUID; + + form + formid = 1, + title = STRING_TOKEN(FORMID1_TITLE); + + checkbox + varid = FormData.CheckboxValue, + prompt = STRING_TOKEN(CHECKBOX_PROMPT), + help = STRING_TOKEN(CHECKBOX_HELP), + endcheckbox; + + numeric + varid = FormData.NumericValue, + prompt = STRING_TOKEN(NUMERIC_PROMPT), + help = STRING_TOKEN(NUMERIC_HELP), + flags = NUMERIC_SIZE_2 | DISPLAY_UINT_HEX, + minimum = 0x1234, + maximum = 0xaa55, + step = 2, + endnumeric; + endform; +endformset; diff --git a/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.c b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.c new file mode 100644 index 0000000..5920b58 --- /dev/null +++ b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include "Data.h" + +extern UINT8 FormBin[]; + +#pragma pack(1) +typedef struct { + VENDOR_DEVICE_PATH VendorDevicePath; + EFI_DEVICE_PATH_PROTOCOL End; +} HII_VENDOR_DEVICE_PATH; +#pragma pack() + +HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + { + (UINT8) (sizeof (VENDOR_DEVICE_PATH)), + (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) + } + }, + FORMSET_GUID + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + { + (UINT8) (END_DEVICE_PATH_LENGTH), + (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) + } + } +}; + + +EFI_HII_HANDLE mHiiHandle = NULL; +EFI_HANDLE mDriverHandle = NULL; +EFI_STRING UEFIVariableName = UEFI_VARIABLE_STRUCTURE_NAME; + + +EFI_STATUS +EFIAPI +HIIFormDataElementsUnload ( + EFI_HANDLE ImageHandle + ) +{ + if (mHiiHandle != NULL) + HiiRemovePackages(mHiiHandle); + + EFI_STATUS Status; + UINTN BufferSize; + UEFI_VARIABLE_STRUCTURE EfiVarstore; + + BufferSize = sizeof(UEFI_VARIABLE_STRUCTURE); + Status = gRT->GetVariable( + UEFIVariableName, + &mHiiVendorDevicePath.VendorDevicePath.Guid, + NULL, + &BufferSize, + &EfiVarstore); + if (!EFI_ERROR(Status)) { + Status = gRT->SetVariable( + UEFIVariableName, + &mHiiVendorDevicePath.VendorDevicePath.Guid, + 0, + 0, + NULL); + if (EFI_ERROR(Status)) { + Print(L"Error! Can't delete variable! %r\n", Status); + } + } + + Status = gBS->UninstallMultipleProtocolInterfaces( + mDriverHandle, + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath, + NULL + ); + + return Status; +} + +EFI_STATUS +EFIAPI +HIIFormDataElementsEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + Status = gBS->InstallMultipleProtocolInterfaces( + &mDriverHandle, + &gEfiDevicePathProtocolGuid, + &mHiiVendorDevicePath, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + UINTN BufferSize; + UEFI_VARIABLE_STRUCTURE EfiVarstore; + BufferSize = sizeof(UEFI_VARIABLE_STRUCTURE); + Status = gRT->GetVariable ( + UEFIVariableName, + &mHiiVendorDevicePath.VendorDevicePath.Guid, + NULL, + &BufferSize, + &EfiVarstore); + if (EFI_ERROR(Status)) { + ZeroMem(&EfiVarstore, sizeof(EfiVarstore)); + Status = gRT->SetVariable( + UEFIVariableName, + &mHiiVendorDevicePath.VendorDevicePath.Guid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof(EfiVarstore), + &EfiVarstore); + if (EFI_ERROR(Status)) { + Print(L"Error! Can't create variable! %r\n", Status); + } + } + + mHiiHandle = HiiAddPackages( + &gEfiCallerIdGuid, + mDriverHandle, + HIIFormDataElementsStrings, + FormBin, + NULL + ); + if (mHiiHandle == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} diff --git a/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.inf b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.inf new file mode 100644 index 0000000..b9c0652 --- /dev/null +++ b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/HIIFormDataElements.inf @@ -0,0 +1,22 @@ +[Defines] + INF_VERSION = 1.25 + BASE_NAME = HIIFormDataElements + FILE_GUID = ab971f73-f582-4f90-a48d-88ff7c884bd9 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HIIFormDataElementsEntryPoint + UNLOAD_IMAGE = HIIFormDataElementsUnload + +[Sources] + HIIFormDataElements.c + Strings.uni + Form.vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiLib + HiiLib diff --git a/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Strings.uni b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Strings.uni new file mode 100644 index 0000000..4e283e1 --- /dev/null +++ b/Lessons/Lesson_66/UefiLessonsPkg/HIIFormDataElements/Strings.uni @@ -0,0 +1,17 @@ +// +// Copyright (c) 2021, Konstantin Aladyshev +// +// SPDX-License-Identifier: MIT +// + +#langdef en-US "English" + +#string FORMSET_TITLE #language en-US "Simple Formset" +#string FORMSET_HELP #language en-US "This is a very simple formset" +#string FORMID1_TITLE #language en-US "Simple Form" +#string CHECKBOX_PROMPT #language en-US "Checkbox prompt" +#string CHECKBOX_HELP #language en-US "Checkbox help" +#string NUMERIC_PROMPT #language en-US "Numeric prompt" +#string NUMERIC_HELP #language en-US "Numeric help" +#string STRING_PROMPT #language en-US "String prompt" +#string STRING_HELP #language en-US "String help" diff --git a/README.md b/README.md index 2a4e6ea..cf09ada 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ These series of lessons are intendend to get you started with UEFI programming i - [Lesson 63](Lessons/Lesson_63): Create HII Form with a checkbox - Part 1: VFR code for `checkbox` element and `efivarstore` element - [Lesson 64](Lessons/Lesson_64): Create HII Form with a checkbox - Part 2: Necessary code for `efivarstore` to work correctly - [Lesson 65](Lessons/Lesson_65): VFR input elements - Part 1: `numeric` +- [Lesson 66](Lessons/Lesson_66): VFR input elements - Part 2: `string` _____ -- cgit v1.2.3-18-g5258