UEFI: Difference between revisions
[unchecked revision] | [unchecked revision] |
Content deleted Content added
m Fix lint errors |
|||
(81 intermediate revisions by 27 users not shown) | |||
Line 2:
== UEFI basics ==
===Downloading UEFI images===
If you choose [[VirtualBox]] for virtualization, then UEFI is already included, no need to download the image manually. You just have to enable it in the VM's preferences by clicking "Settings" / "Systems" / "Enable EFI (special OSes only)" checkbox.
Otherwise for emulation and virtual machines, you'll need an OVMF.fd firmware image. It might be tricky to find, so here are some alternative download links too:
* [https://github.com/tianocore/tianocore.github.io/wiki/OVMF TianoCore Download] (official link)
* [https://github.com/BlankOn/ovmf-blobs OVMF-blobs] (unofficial precompiled 32 bit and 64 bit versions, easy to use)
* [https://www.kraxel.org/repos/ RPM packages] (official TianoCore packages)
* [https://packages.debian.org/sid/ovmf Debian packages]
* [https://packages.gentoo.org/useflags/ovmf Gentoo packages]
* [https://aur.archlinux.org/packages/ovmf-git/?comments=all Arch packages]
Under Linux, you can also install these with your distro's package manager, for example:
'''Debian / Ubuntu'''
<syntaxhighlight lang="bash">
# apt-get install ovmf
</syntaxhighlight>
'''RedHat / CentOS'''
<syntaxhighlight lang="bash">
# yum install ovmf
</syntaxhighlight>
'''MacOS'''
Use the OVMF-blobs repo.
'''Windows'''
Use the OVMF-blobs repo or download the RPM version, then use ''7-Zip File Manager'' to extract the OVMF.fd file from the downloaded archive.
===UEFI vs. legacy BIOS===
A common misconception is that UEFI
====Platform initialization====
On a legacy system, BIOS performs all the usual platform initialization (memory controller configuration, PCI bus configuration and BAR mapping, graphics card initialization, etc.), but then drops into a backwards-compatible real mode environment. The bootloader must enable the A20 gate, configure a GDT and an IDT, switch to protected mode, and for x86-64 CPUs, configure paging and switch to long mode.
UEFI firmware performs those same steps, but it also enables the A20 gate and prepares
Additionally, the platform initialization procedure of UEFI firmware is standardized. This allows UEFI firmware to be extended in a vendor-neutral way.
====Boot mechanism====
A legacy BIOS loads a 512 byte flat binary blob from the MBR of the boot device into memory at physical address 7C00 and jumps to it. The bootloader cannot return back to BIOS. UEFI firmware loads an arbitrary sized UEFI application (a relocatable PE executable file) from a FAT partition on a GPT
====System discovery====
A legacy bootloader scans memory for structures like the [[EBDA]], [[SMBIOS]], and [[ACPI]] tables. It uses PIO to talk to the root [[PCI]] controller and scan the PCI bus. It is possible that redundant tables may be present in memory (for example, the [[MP_Specification|MP]] table in the SMBIOS contains information that's also present in the ACPI [[DSDT]] and [[MADT]]) and the bootloader can choose which to use.
When UEFI firmware calls a UEFI application's entry point function, it passes a "System Table" structure, which contains pointers to all of the system's ACPI tables, memory map, and other information relevant to an OS. Legacy tables (like
====Convenience functions====
Line 27 ⟶ 58:
====Development environment====
Legacy bootloaders can be developed in any environment that can generate flat binary images:
[[EDK2]] is a large and complex, yet feature filled environment with its own build system. It can be configured to use GCC, LLVM, MinGW, Microsoft Visual C++, etc. as a cross-compiler. Not only can it be used to compile UEFI applications, but it can also be used to compile UEFI firmware to be flashed to a BIOS ROM.
[[
[[POSIX-UEFI]] is very similar to GNU-EFI, but it is distributed mainly as a source, not as a binary library, has ANSI C like names and works with GCC as well as LLVM CLang. It's shipped with a Makefile that sets up the compiler flags for you.
====Emulation====
[[OVMF]], a popular open source UEFI firmware, has been ported to the QEMU (but not Bochs) emulated machine. Because it implements the UEFI specification, it behaves very similarly to commercial UEFI firmware on real machines. (OVMF itself is built with TianoCore, and its source ship with is as well, but pre-built images are available.)
===Legacy bootloader or UEFI application?===
Line 79 ⟶ 111:
Note that UEFI applications are not signed by the PK, unless the PK also happens to be in the KEK.
Not all UEFI firmwares support Secure Boot, although it is a requirement for Windows
An easy way out to use a loader that is signed by Microsoft, and allows you to load another binary signed by a key and certificate owned by you (called MOK, Machine Owner's Key). Such a loader is [https://github.com/rhboot/shim shim], used by RedHat, Fedora, Suse, Ubuntu, Arch and many other distros to load GRUB. The filename of the EFI executable is hardwired in shim, but if you rename your loader to GRUBX64.EFI (or GRUBIA32.EFI), you sign it with your MOK key and certificate using [https://github.com/imedias/sbsigntool sbsigntool], then you can load any loader in Secure Boot you want.
===How to use UEFI===
Line 87 ⟶ 121:
A good starting point is writing a UEFI application that uses the System Table to fetch a memory map, and uses the "File" protocol to read files from FAT-formatted disks. The next step might be to use the System Table to locate ACPI tables.
==Developing with POSIX-UEFI==
:{{Main|POSIX-UEFI}}
One option to compile UEFI applications on POSIX like systems is POSIX-UEFI. It provies a [[libc]]-like API for your EFI application, and ships with a Makefile that can detect and set up the toolchain for you. It can use GCC or LLVM, and defaults to using the host compiler, but a cross compiler is still recommended.
It uses POSIX style typedefs (like ''uintn_t'' instead of ''UINTN''), and it does not ship with the standard EFI headers. You can still get interfaces not covered by POSIX-UEFI (such as GOP) by installing the EFI headers from GNU-EFI or EDK2. Also, it compiles with the MS ABI, meaning that UEFI services can be called natively (i.e., without uefi_call_wrapper) so long as your apps are compiled with it as well.
The traditional "Hello, world" UEFI program goes like this.
<syntaxhighlight lang="c">
#include <uefi.h>
int main (int argc, char **argv)
{
printf("Hello, world!\n");
return 0;
}
</syntaxhighlight>
Makefile looks like this:
<syntaxhighlight lang="make">
TARGET = main.efi
include uefi/Makefile
</syntaxhighlight>
Run make to build it. The result of this process is a PE executable file ''main.efi''.
==Developing with GNU-EFI==
:{{Main|GNU-EFI}}
GNU-EFI can be used to develop both 32-bit and 64-bit UEFI applications. This section will address 64-bit UEFI applications only, and assumes that the development environment itself is running on an x86_64 system, so that no cross-compiler is needed. For a more thorough walk-through of a proper (non-gnu-efi) development environment, see [[UEFI App Bare Bones]].
GNU-EFI includes four things:
* '''crt0-efi-x86_64.o''': A CRT0 (C runtime initialization code) that provides an entry point that UEFI firmware will call when launching the application, which will in turn call the "efi_main" function that the developer writes.
* '''libgnuefi.a''': A library containing a single function (''_relocate'') that is used by the CRT0.
* '''elf_x86_64_efi.lds''': A linker script used to link ELF binaries into UEFI applications.
* '''efi.h''' and other headers: Convenience headers that provide structures, typedefs, and constants improve readability when accessing the System Table and other UEFI resources.
* '''libefi.a''': A library containing convenience functions like CRC computation, string length calculation, and easy text printing.
Line 102 ⟶ 161:
The traditional "Hello, world" UEFI program is shown below.
<
#include <efi.h>
#include <efilib.h>
Line 114 ⟶ 173:
return EFI_SUCCESS;
}
</syntaxhighlight>
A few notes:
* efi.h is included so we can use types like EFI_STATUS, EFI_HANDLE, and EFI_SYSTEM_TABLE.
* When creating a 32-bit UEFI application, EFIAPI is empty; GCC will compile the "efi_main" function using the standard C calling convention. When creating a 64-bit UEFI application, EFIAPI expands to "__attribute__((ms_abi))" and GCC will compile the "efi_main" function using Microsoft's x64 calling convention, as specified by UEFI. Only functions that will be called directly from UEFI (
* "InitializeLib" and "Print" are convenience functions provided by libefi.a with prototypes in efilib.h. "InitializeLib" lets libefi.a store a reference to the ImageHandle and SystemTable provided by BIOS. "Print" uses those stored references to print a string by reaching out to UEFI-provided functions in memory. (Later on we will see how to find and call UEFI-provided functions manually.)
This program is compiled and linked as below.
<
$ gcc main.c \
-c \
Line 141 ⟶ 200:
-shared \
-Bsymbolic \
-
-l:
-l:libefi.a \
-o main.so
Line 156 ⟶ 216:
main.so \
main.efi
</syntaxhighlight>
The result of this process is a 44 kB PE executable file ''main.efi''. On a real project you'll probably want to use make or another build tool, and may need to build a cross-compiler. The way GNU-EFI works is a bit contrived: you are wrapping an ELF file built by your normal compiler into PE.
==Emulation with QEMU and OVMF==
Any recent version of QEMU with a recent version of OVMF will be sufficient to run a UEFI application. QEMU binaries are available for many platforms, and a binary OVMF image (OVMF.fd) can be found on the [http://www.tianocore.org/ovmf/ TianoCore] website. QEMU (without any boot disk) can be invoked as below. (To prevent recent versions of QEMU from attempting a PXE (network) boot when no boot disk is found, use <code>-net none</code>.
The recommended way to use OVMF (for QEMU 1.6 or newer) is with a <code>pflash</code> parameter. The instructions below assume you have an OVMF image split into separate CODE and VARS sections.
<syntaxhighlight lang="bash">
$ qemu-system-x86_64 -cpu qemu64 \
-drive if=pflash,format=raw,unit=0,file=path_to_OVMF_CODE.fd,readonly=on \
-drive if=pflash,format=raw,unit=1,file=path_to_OVMF_VARS.fd \
-net none
</syntaxhighlight>
If you prefer to work on a terminal without a display, or via SSH/telnet, you will want to run QEMU without graphics support, using the <code>-nographic</code> flag.
If OVMF does not find a boot disk with a properly named UEFI application (more on this later) it will drop into a UEFI shell.
Line 178 ⟶ 241:
===Creating disk images===
:{{Main|Bootable Disk}}
To launch a UEFI application you will need to create a disk image and present it to QEMU. UEFI firmware expects UEFI applications to be stored in a FAT12, FAT16, or FAT32 file system on a [[GPT]] or [[MBR]]-partitioned disk. Many firmwares only support FAT32, so that's what you'll want to use. Depending on your platform, there are several different ways to create a disk image containing your UEFI application, but they all start by creating a zeroed disk image file. The minimum FAT32 partition size is 33,548,800 bytes, plus you will need space for the primary and secondary GPT tables, plus some slack space so the partition can be aligned correctly. Throughout these examples we will be creating a 48,000,000 byte (93750 512-byte sectors, or 48 MB) disk image.
<syntaxhighlight lang="bash">
$ dd if=/dev/zero of=/path/to/uefi.img bs=512 count=93750
</syntaxhighlight>
====uefi-run helper application====
The uefi-run application is useful for quick testing. It creates a temporary FAT image containing your EFI application and starts qemu.
<syntaxhighlight lang="bash">
$ uefi-run -b /path/to/OVMF.fd -q /path/to/qemu app.efi -- <extra_qemu_args>
</syntaxhighlight>
uefi-run is not currently packaged for any distribution. You can install it using cargo (the Rust package manager) though ("cargo install uefi-run").
====Linux, root required====
This approach requires root privileges and uses '''gdisk''', '''losetup''', and '''mkdosfs'''. First, use gdisk to create a GPT partition table with a single [[EFI System Partition]].
<syntaxhighlight lang="bash">
$ gdisk /path/to/uefi.img
GPT fdisk (gdisk) version 0.8.10
Partition table scan:
MBR: not present
BSD: not present
APM: not present
GPT: not present
Creating new GPT entries.
Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y
Command (? for help): n
Partition number (1-128, default 1): 1
First sector (34-93716, default = 2048) or {+-}size{KMGTP}: 2048
Last sector (2048-93716, default = 93716) or {+-}size{KMGTP}: 93716
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): ef00
Changed type of partition to 'EFI System'
Command (? for help): w
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!
Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to uefi.img.
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot.
The operation has completed successfully.
</syntaxhighlight>
Now you have disk image with [[GPT|GUID partition table]] on it and an unformatted EFI partition starting at sector 2048. Unless you deviated from the commands shown above, the disk image will use 512-byte sectors, so the EFI partition starts at byte 1,048,576 and is 46,934,528 bytes in length. Use losetup to present the partition to Linux on a loopback device.
<syntaxhighlight lang="bash">
losetup --offset 1048576 --sizelimit 46934528 /dev/loop0 /path/to/uefi.img
</syntaxhighlight>
(If /dev/loop0 is already in use you will need to select a different loopback device.)
Format the partition for FAT32 with mkdosfs.
<syntaxhighlight lang="bash">
mkdosfs -F 32 /dev/loop0
</syntaxhighlight>
The partition can now be mounted, so that we can copy files to it. In this example we use the "/mnt" directory, but you could also create a local directory for temporary use.
<syntaxhighlight lang="bash">
mount /dev/loop0 /mnt
</syntaxhighlight>
Copy any UEFI applications you want to test to the file system.
<syntaxhighlight lang="bash">
$ cp /path/to/main.efi /mnt/
$ ...
</syntaxhighlight>
Finally, unmount the partition and free the loopback device.
<syntaxhighlight lang="bash">
$ umount /mnt
$ losetup -d /dev/loop0
</syntaxhighlight>
''uefi.img'' is now a disk image containing primary and secondary GPT tables, containing a single partition of type EFI, containing a FAT32 file system, containing one or more UEFI applications.
====Linux, root not required====
This approach uses '''parted''', '''mformat''', and '''mcopy''' and can be performed with user privileges. First, use parted to create primary and secondary GPT headers, and a single EFI partition spanning the same range as the approach above.
<syntaxhighlight lang="bash">
$ parted /path/to/uefi.img -s -a minimal mklabel gpt
$ parted /path/to/uefi.img -s -a minimal mkpart EFI FAT16 2048s 93716s
$ parted /path/to/uefi.img -s -a minimal toggle 1 boot
</syntaxhighlight>
Now create a new temporary image file that will contain the EFI partition data and use mformat to format it with FAT16.
<syntaxhighlight lang="bash">
dd if=/dev/zero of=/tmp/part.img bs=512 count=91669
mformat -i /tmp/part.img -h 32 -t 32 -n 64 -c 1
</syntaxhighlight>
Use mcopy to copy any UEFI applications you want to test to the file system.
<syntaxhighlight lang="bash">
$ mcopy -i /tmp/part.img /path/to/main.efi ::
$ ...
</syntaxhighlight>
Finally, write the partition image into the main disk image.
<syntaxhighlight lang="bash">
$ dd if=/tmp/part.img of=/path/to/uefi.img bs=512 count=91669 seek=2048 conv=notrunc
</syntaxhighlight>
''uefi.img'' is now a disk image containing primary and secondary GPT tables, containing a single partition of type EFI, containing a FAT16 file system, containing one or more UEFI applications.
====FreeBSD, root required====
This approach requires root privileges and uses '''mdconfig''', '''gpart''', '''newfs_msdos''', and '''mount_msdosfs'''. First, create a device node that presents the zeroed disk image as a block device. This will let us work on it using standard partitioning and formatting tools.
<
$ mdconfig -f /path/to/uefi.img
md0
</syntaxhighlight>
In this example the new block device is ''md0''. Now create the empty primary and secondary GPT tables on the device.
<
$ gpart create -s GPT md0
md0 created
</syntaxhighlight>
Now we can add a partition to the disk. We'll specify an "EFI" partition, which just means that GPT will set that partition's GUID to the special "EFI" type. Not all BIOSs require this, and the partition will still be able to be mounted and browsed normally on Linux, FreeBSD, and Windows.
<
$ gpart add -t efi md0
md0p1 added
</syntaxhighlight>
Next, create a FAT16 file system on the new partition. You can specify various parameters for the file system if you'd like, but it isn't necessary. Ideally you would create a FAT32 partition
<
$ newfs_msdos -F 16 md0p1
newfs_msdos: trim
/dev/
BytesPerSec=512 SecPerClust=8 ResSectors=1 FATs=2 RootDirEnts=512 Media=0xf0 FATsecs=
</syntaxhighlight>
The partition can now be mounted, so that we can copy files to it. In this example we use the ''/mnt'' directory, but you could also create a local directory for temporary use.
<
$ mount_msdosfs /dev/md0p1 /mnt
</syntaxhighlight>
Copy any UEFI applications you want to test to the file system.
<
$ cp /path/to/main.efi /mnt/
$ ...
</syntaxhighlight>
Finally, unmount the partition and free the block device.
<
$ umount /mnt
$ mdconfig -d -u md0
</syntaxhighlight>
''uefi.img'' is now a disk image containing primary and secondary GPT tables, containing a single partition of type EFI, containing a FAT16 file system, containing one or more UEFI applications.
====Mac OS (root not required)====
Mac OS has a single tool (hdiutil) that creates the disk image and copy files at same time.
Let's say that you're creating a UEFI boot for x86_64. By definition the file name should be BOOTX64.EFI and this file should be located in the /EFI/BOOT folder.
First, let's create a temporary folder that will contains all files and folders required for booting UEFI.
<syntaxhighlight lang="bash">
$ mkdir -p diskImage/EFI/BOOT
</syntaxhighlight>
Secondly, let's copy the boot application to the required location:
<syntaxhighlight lang="bash">
$ cp bootx64.efi diskImage/EFI/BOOT/BOOTX64.EFI
</syntaxhighlight>
Finally, let's create a disk image partitioned with GPT, formatted with fat32 (-fs fat32), overriding destination file if needed (-ov), define disk size (-size 48m), define volume name (-volname NEWOS), the file format which the disk will be encoded (-format UDTO - the same used for DVDs/CDs) and the source folder containing the files that will be copied to the new disk:
<syntaxhighlight lang="bash">
$ hdiutil create -fs fat32 -ov -size 48m -volname NEWOS -format UDTO -srcfolder diskImage uefi.cdr
</syntaxhighlight>
uefi.cdr should be ready to be used by QEMU.
===Launching UEFI applications===
Once your disk image is ready, you can invoke QEMU as below.
<
$ qemu-system-x86_64 -cpu qemu64 -bios /path/to/OVMF.fd -drive file=uefi.disk,if=ide
</syntaxhighlight>
When OVMF drops into the UEFI shell, you will see an additional entry in the "Mapping table", labeled "FS0". This indicates that the firmware detected the disk, discovered the partition, and was able to mount the file system. You can explore the file system by switching to it using the DOS-style syntax "FS0:", as illustrated below.
Line 317 ⟶ 444:
===Debugging===
OVMF can be built in debug mode, and it will output logging messages to IO port 0x402.
You can use some flags like the ones below to capture the output.
<code>-debugcon file:uefi_debug.log -global isa-debugcon.iobase=0x402</code>
Note that release builds will not output debug messages, or will have reduced output.
See [[Debugging UEFI applications with GDB]].
==Running on real hardware==
===NVRAM variables===
{{Main|UEFI NVRAM}}
A UEFI firmware will present most of its configuration options through a text or graphical configuration menu, just like a legacy BIOS. Selections made in these menus are stored in the NVRAM chip between reboots. Unlike legacy BIOS, however, the firmware developer has the option to expose some or all of these "NVRAM variables" to the OS and end-user via convenience functions made resident in RAM by the firmware at boot.
Line 326 ⟶ 461:
===Bootable UEFI applications===
The boot order NVRAM variables determine where firmware will look for UEFI applications to be launched at boot. Although this can be changed (for example, an OS installer might customize the boot entry for the hard drive to which it was installed) firmware typically looks for a UEFI application named "
Unlike a UEFI application launched from the shell, if a bootable UEFI application returns BIOS will continue searching for other boot devices.
Line 340 ⟶ 475:
* Phoenix (SecureCore, TrustedCore, AwardCore).
* Insyde (InsydeH20).
== UEFI applications in detail ==
=== Binary Format ===
UEFI executables are regular PE32 / PE32+ (Windows x32 / x64) images, with a specific '''subsystem'''.
Every UEFI application is basically a windows EXE (or DLL) without symbol tables.
{| class="wikitable"
|+ Types of UEFI images
|-
! Type
! Description
! Subsystem
|-
| Applications || OS loaders and other utility programs. || 10
|-
| Boot service driver || Drivers used by the firmware when booting (e.g. disk drivers, network drivers). || 11
|-
| Runtime driver || Drivers which may stay loaded even after the OS loads and exits the boot services. || 12
|}
UEFI images must also specify the type of machine code they contain. A UEFI loader will refuse to boot an incompatible image.
{| class="wikitable"
|+ Types of machines
|-
! Name / arch
! Value
|-
|x86||0x014c
|-
|x86_64||0x8664
|-
|Itanium x64||0x0200
|-
|UEFI Byte Code||0x0EBC
|-
|ARM<sup>1</sup>||0x01C2
|-
|AArch (ARM x64)||0xAA64
|-
|RISC-V x32||0x5032
|-
|RISC-V x64||0x5064
|-
|RISC-V x128||0x5128
|}
[1] ARM means you can use Thumb / Thumb 2 instructions, but UEFI interfaces are in ARM mode.
==== Initialization ====
Applications must either load an OS and exit boot services, or return from the main function (in which case the boot loader will look for the next app to load).
Drivers must initialize and then return 0 on success, or an error code. A computer might fail to boot if a required driver fails to load.
==== Memory ====
The memory map returned by UEFI will mark the memory areas which drivers use.
Once your OS loader finished, your kernel is allowed to reuse the memory where the boot loader was loaded.
The memory types are <code>Efi{Loader/BootServices/RuntimeServices}{Code/Data}</code>.
After exiting the boot services, you may reuse whatever non-read-only memory the boot drivers used.
However, memory used by the runtime drivers must '''never''' be touched - the runtime drivers stay active and loaded for as long as the computer runs.
One way to see a breakdown of a PE file containing a UEFI application is by<syntaxhighlight lang="bash">$ objdump --all-headers /path/to/main.efi</syntaxhighlight>
Its output is quite long. Among other things, it shows the '''subsystem''', that is the type of the UEFI image, mentioned earlier.
=== Calling Conventions ===
Line 358 ⟶ 547:
* '''cdecl''' for x86 UEFI functions
* '''Microsoft's 64-bit calling convention''' for x86-64 UEFI functions
*
This has two impacts on UEFI application developers:
Line 366 ⟶ 554:
Note that functions strictly internal to the application can use whatever calling convention the developer chooses.
==== POSIX-UEFI, GNU-EFI and GCC ====
{{Main|GNU-EFI}}
cdecl is the standard calling convention used by GCC, so no special attributes or modifiers are needed for writing the main entry point or calling UEFI functions in an x86 UEFI application developed with GNU-EFI. For x86-64, however, the entry point function must be declared with the "___attribute___((ms_abi))" modifier and all calls to UEFI-provided functions must be made through the "uefi_call_wrapper" thunk. This thunk is called with cdecl, but then translates to the Microsoft x86-64 calling convention before calling the requested UEFI function. This is necessary because older releases of GCC do not support specifying calling conventions for function pointers.
For [[POSIX-UEFI]], which also uses GCC, your entry point looks like the standard main(), and no special ABI is required. Also the build environment takes care of the compiler flags for you, so you can simply call UEFI functions without "uefi_call_wrapper", no matter if you're using the host gcc or a cross-compiler.
For developer convenience, both POSIX-UEFI and GNU-EFI provides the "EFIAPI" macro, which expands to "cdecl" when targeting x86 and "__attribute__(ms_abi))" when targeting x86-64. Additionally, the "uefi_call_wrapper" thunk will simply pass the call through on x86. This allows the same source code to target x86 and x86-64. For example, the following main function will compile with the correct calling convention on both x86 and x86-64 and the call through the "uefi_call_wrapper" thunk will select the correct calling convention to use when calling the UEFI function (in this case, printing a string).
<syntaxhighlight lang="c">
EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
Line 379 ⟶ 570:
return status;
}
</syntaxhighlight>
=== Language binding ===
UEFI applications are typically written in C, although bindings could be written for any other language that compiles to machine code. Assembly is also an option; a [[uefi.inc]] is available for [[FASM]] that allows UEFI applications to be written as below.
<
format pe64 dll efi
entry main
Line 404 ⟶ 595:
section '.data' data readable writeable
_hello
section '.reloc' fixups data discardable
</syntaxhighlight>
As a UEFI application contains normal x86 or x86-64 machine code, inline assembly is also an option in compilers that support it.
=== EFI Byte Code===
UEFI also includes a virtual machine specification based on a byte code format called EFI Byte Code (EBC), which can be used to write platform-independent device drivers, but not UEFI applications. As of 2015 there has been limited use of EBC.
== Common Problems ==
Line 418 ⟶ 609:
=== My UEFI application hangs/resets after about 5 minutes ===
When control is handed to your UEFI application by firmware, it sets a watchdog timer for 5 minutes, after which the firmware is reinvoked as it assumes your application has hung. The firmware in this case will normally try to reset the system (although the OVMF firmware in VirtualBox simply causes the screen to go black and hang). To counteract this, you are required to refresh the watchdog timer before it times out. Alternatively, you can disable it completely with code like <
=== My bootloader hangs if I use user defined EFI_MEMORY_TYPE values ===
For the memory management functions in EFI, an OS is meant to be able to use "memory type" values above 0x80000000 for its own purposes. In the
== See also ==
===
* [[UEFI Bare Bones]]
* [[EFI System Partition]]
* [[PE]] file format
* [[
* [[POSIX-UEFI]]
* [[GNU-EFI]]
* [https://github.com/nebulaeonline/nebulae/tree/UefiBarebones Uefi Barebones MSVC/Clang/Visual Studio]
=== External Links ===
* [
* [http://
* [
* [https://web.archive.org/web/20181028201454/http://wiki.phoenix.com/wiki/index.php/Main_Page Phoenix UEFI Wiki]
* [http://x86asm.net/articles/others/index.html Several articles about UEFI]
* [
* [
* [https://web.archive.org/web/20160316192235/http://internshipatdell.wikispaces.com/file/view/How+to+build+an+UEFI+application.pptx Presentation guiding through simple UEFI application setup]
* [
* [https://gitlab.com/bztsrc/posix-uefi POSIX-UEFI] documentation and source
* [[Wikipedia:Extensible_Firmware_Interface|Wikipedia Article on EFI]]
[[Category:
[[Category:
[[Category:ARM]]
[[Category:Firmware]]
[[Category:UEFI]]
[[de:Unified_Extensible_Firmware_Interface]]
|