diff options
author | Konstantin Aladyshev <aladyshev22@gmail.com> | 2021-07-10 00:04:40 +0300 |
---|---|---|
committer | Konstantin Aladyshev <aladyshev22@gmail.com> | 2021-07-10 00:04:40 +0300 |
commit | 6064c1e48b622f53538f4df9bdd402c607a87d51 (patch) | |
tree | 93d3c937b9568568307fd2ff7053a30c538ad72a /Lessons/Lesson_03/README.md | |
parent | a9c375c80c3505be794ec2b5d5bb90de27ef0d42 (diff) | |
download | UEFI-Lessons-6064c1e48b622f53538f4df9bdd402c607a87d51.tar.gz UEFI-Lessons-6064c1e48b622f53538f4df9bdd402c607a87d51.tar.bz2 UEFI-Lessons-6064c1e48b622f53538f4df9bdd402c607a87d51.zip |
Move lessons to separate folder
Signed-off-by: Konstantin Aladyshev <aladyshev22@gmail.com>
Diffstat (limited to 'Lessons/Lesson_03/README.md')
-rw-r--r-- | Lessons/Lesson_03/README.md | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/Lessons/Lesson_03/README.md b/Lessons/Lesson_03/README.md new file mode 100644 index 0000000..af1d1ad --- /dev/null +++ b/Lessons/Lesson_03/README.md @@ -0,0 +1,229 @@ +Finally we are ready to write our "Hello World" app. + +First we create a new edk2 module in our package directory similar to the our `SimplestApp` module: +``` +$ mkdir UefiLessonsPkg/HelloWorld +$ vi UefiLessonsPkg/HelloWorld/HelloWorld.inf +[Defines] + INF_VERSION = 1.25 + BASE_NAME = HelloWorld + FILE_GUID = 2e55fa38-f148-42d3-af90-1be247323e30 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +[Sources] + HelloWorld.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint +``` +Don't forget to add our newly created app to the `Components` section of the package DSC file +``` +[Components] + UefiLessonsPkg/SimplestApp/SimplestApp.inf ++ UefiLessonsPkg/HelloWorld/HelloWorld.inf +``` +Next we need to write the source code file. Let's remember the code for our SimplestApp: +``` +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_SUCCESS; +} +``` +To print something to the console ("Hello World" message in our case) we need to use services from the `EFI_SYSTEM_TABLE` that is passed to the entry point of our app. + +The description of the `EFI_SYSTEM_TABLE` can be found in the UEFI specification (https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf). + +EFI_SYSTEM_TABLE is a struct that was populated by the UEFI firmware and contains pointers to the runtime and boot services tables. +``` +typedef struct { + EFI_TABLE_HEADER Hdr; + CHAR16 *FirmwareVendor; + UINT32 FirmwareRevision; + EFI_HANDLE ConsoleInHandle; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + EFI_HANDLE ConsoleOutHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + EFI_HANDLE StandardErrorHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; + EFI_RUNTIME_SERVICES *RuntimeServices; + EFI_BOOT_SERVICES *BootServices; + UINTN NumberOfTableEntries; + EFI_CONFIGURATION_TABLE *ConfigurationTable; +} EFI_SYSTEM_TABLE; +``` +We are interested in the `ConOut` field. `ConOut` is abbreviaton for "Console Output" and according to the UEFI spec it is a pointer to the `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` interface that is associated with `ConsoleOutHandle`. + +If we keep digging into UEFI spec we can find description of the `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL`. +According to the spec `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` defines the minimum requirements for a text-based ConsoleOut device. + +As everything in UEFI it has GUID: +``` +#define EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID \ + {0x387477c2,0x69c7,0x11d2,\ + {0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b}} +``` + +And the interface description is: +``` +typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { + EFI_TEXT_RESET Reset; + EFI_TEXT_STRING OutputString; + EFI_TEXT_TEST_STRING TestString; + EFI_TEXT_QUERY_MODE QueryMode; + EFI_TEXT_SET_MODE SetMode; + EFI_TEXT_SET_ATTRIBUTE SetAttribute; + EFI_TEXT_CLEAR_SCREEN ClearScreen; + EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition; + EFI_TEXT_ENABLE_CURSOR EnableCursor; + SIMPLE_TEXT_OUTPUT_MODE *Mode; +} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; +``` +Right now we are interested in a `OutputString` method: +``` +OutputString Displays the string on the device at the current cursor location. +``` +This is what we need. Let's look at the function description: +``` +EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString() + +Summary +Writes a string to the output device. + +Prototype +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_STRING) ( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *String + ); + +Parameters +This A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL instance. +String The Null-terminated string to be displayed on the output device(s). +``` + +In edk2 `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` is defined in the header file: +https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/SimpleTextOut.h + + +With all this knowledge we can write our source code file `UefiLessonsPkg/HelloWorld/HelloWorld.c`: +``` +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n"); + return EFI_SUCCESS; +} +``` + +The `L""` signifies that the string is composed from CHAR16 symbols, as was required in spec. + +As for `CHAR16` - UEFI uses special names like these for simple types. It is a proxy for a different type realization in different processor architectures. +When we compile our code code for X64, our realization would be picked from a file https://github.com/tianocore/edk2/blob/master/MdePkg/Include/X64/ProcessorBind.h: +``` +typedef unsigned short CHAR16; +``` +For example RISCV would define it like this (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/RiscV64/ProcessorBind.h): +``` +typedef unsigned short CHAR16 __attribute__ ((aligned (2))); +``` + +All the simple types for X64: +``` + /// + /// 8-byte unsigned value + /// + typedef unsigned long long UINT64; + /// + /// 8-byte signed value + /// + typedef long long INT64; + /// + /// 4-byte unsigned value + /// + typedef unsigned int UINT32; + /// + /// 4-byte signed value + /// + typedef int INT32; + /// + /// 2-byte unsigned value + /// + typedef unsigned short UINT16; + /// + /// 2-byte Character. Unless otherwise specified all strings are stored in the + /// UTF-16 encoding format as defined by Unicode 2.1 and ISO/IEC 10646 standards. + /// + typedef unsigned short CHAR16; + /// + /// 2-byte signed value + /// + typedef short INT16; + /// + /// Logical Boolean. 1-byte value containing 0 for FALSE or a 1 for TRUE. Other + /// values are undefined. + /// + typedef unsigned char BOOLEAN; + /// + /// 1-byte unsigned value + /// + typedef unsigned char UINT8; + /// + /// 1-byte Character + /// + typedef char CHAR8; + /// + /// 1-byte signed value + /// + typedef signed char INT8; +``` + + +Let's finally compile our edk2 module: +``` +$ build --platform=UefiLessonsPkg/UefiLessonsPkg.dsc \ + --module=UefiLessonsPkg/HelloWorld/HelloWorld.inf \ + --arch=X64 \ + --buildtarget=RELEASE --tagname=GCC5 +``` + +Copy the app to our `UEFI_disk` folder and run OVMF: +``` +$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/HelloWorld.efi ~/UEFI_disk/ +$ qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \ + -drive format=raw,file=fat:rw:~/UEFI_disk \ + -nographic \ + -net none +``` + +``` +UEFI Interactive Shell v2.2 +EDK II +UEFI v2.70 (EDK II, 0x00010000) +Mapping table + FS0: Alias(s):HD0a1:;BLK1: + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) + BLK0: Alias(s): + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) + BLK2: Alias(s): + PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) +Press ESC in 4 seconds to skip startup.nsh or any other key to continue. +Shell> fs0: +FS0:\> HelloWorld.efi +Hello World! +FS0:\> +``` |