In the late 90s, Number Nine Visual Technology, the then design graphics card designer, offered a VGA BIOS for its PCI devices on the site. There is nothing remarkable in this event. Is that the video card Number Nine could work on IBM PC-compatible platforms, and in MAC-systems using Power PC. Therefore, the same device was packaged with different BIOS files.
Most likely, then it could not be otherwise. How is the situation with the support of devices designed to work in different hardware environments? The answer to this question is given by the UEFI specification, within which an elegant solution is proposed -
EFI Byte Code or EBC. With it, you can create cross-platform applications for firmware.
How does EBC work
In the framework of the UEFI-standard, the virtual machine architecture of the register type
EFI Byte Code Virtual Machine is defined. The command interpreter is included in the firmware of the motherboard. The firmware of the expansion cards is written in the command system of the virtual machine, ideally without using instructions from the central processor. Thus, the expansion card will work on any motherboard that supports EBC, regardless of the type of CPU. Today, their list does not shine with a variety: as usual, there is AMD and Intel in 32-bit and 64-bit versions, Itanium, ARM.
')
EBC virtual processor architecture
The 64-bit EBC virtual processor contains 8 general-purpose registers (R0-R7), supports direct, indirect, and direct addressing of operands. The command system includes arithmetic and logical operations, shifts, operand transfers with support for character expansion, conditional and unconditional control transfer, subroutine calls and returns, as well as a number of auxiliary operations. The stack is supported, with the stack pointer (register R0) according to the traditions of the x86 architecture, classified as a general-purpose register. It is noteworthy that the special form of the CALL instruction allows you to call out subroutines written in the
native language of the platform from EBC routines, because sometimes such a need does arise. In the same way, procedures for supporting UEFI protocols can be called from the EBC programs using the transmission model of the input and output parameters, independent of the type of central processor.
We start experiments
The proposed example is “
Hello, EBC! "Is a UEFI application written in the EBC Byte Code virtual machine command system. As mentioned above, the EBC command interpreter, which allows you to run modules of this type, is resident in the UEFI firmware of the motherboard. Using EBC instead of machine code allows you to create cross-platform applications and drivers, including the firmware of various expansion cards, which makes these devices compatible with platforms that use CPUs other than x86 architecture.
Explanations for example
The program displays a text message using the line output procedure from the
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL function
set . Let us consider in more detail its
source code .
To call this EFI-function in the stack, you need to pass two parameters: a pointer to the interface unit of the protocol used and a pointer to a string represented in the UNICODE format. These parameters are prepared in registers R1 and R2, then pushed onto the stack with PUSHn instructions, starting with the last parameter. Then, in the R3 register, the address for the procedure call is read, read from the interface unit of the protocol used, and the target procedure for the line output is called. After returning, we release the stack with POPn instructions.
System tables and cross-platform
To call UEFI protocol service procedures, applications use pointers located in the UEFI system tables and various interface blocks. In 32-bit UEFI implementations, 4 byte pointers are used, and in 64-bit implementations, 8 bytes are used. Consequently, the address of the pointer inside the table will depend on the width of the CPU. How is cross-platform provided?
Consider an example of an instruction transferring to the R1 register the contents of a memory cell whose address is equal to the initial value of the R1 register plus the
offset .
MOVnw R1,@R1(+5,+24)
The offset is given as two terms: +5 and +24.
The first term +5 is the indexable pointer number. The EBC command interpreter multiplies this value by the pointer size, which is 4 for 32-bit UEFI implementations and 8 for 64-bit implementations.
The second term +24 is a constant independent of the type of platform. It is used to set the size of the header for the
EFI_SYSTEM_TABLE table.
Similarly, PUSHn (Push Natural) instructions work when preparing a stack frame for called procedures. The width of the parameters written to the stack (32 or 64 bits) depends on the width of the CPU. This provides a gateway between the EBC code of the application and the procedures that are part of the UEFI firmware written in the system commands of the central processor.
Broadcast and launch
FASM 1.69.50 is used to
broadcast the program and generate the EBC application. The EFI Byte Code virtual machine instructions are given as hexadecimal constants. Guided by research interest, we deliberately abandoned the use of high-level languages and wrote our example in the EBC assembler. At the same time, we had to solve several problems related to the fact that the FASM compiler does not support EBC.
After the translation, in the header of the file
helloebc.efi , at addresses 84h, 85h, bytes 64h, 86h must be replaced by BCh, 0Eh. Thus, the
Machine Type field, initially containing 8664h (x86-64 machine), is replaced by 0EBCh (EBC machine). To run the editor embedded in the UEFI Shell, type the following at the command line:
hexedit helloebc.efi .
Figure 1 .
Correction of the Machine Type field in the application headerAfter that, you can run the application.
Figure 2 .
Application ResultThe application can also be run under the Intel EBC Debugger.
Figure 3 .
Loading the debugger with the load command and running the EBC application under the Intel EBC Debugger debuggerSummary
We tested the test case in IA32 EFI and x64 UEFI environments. Theoretically, it should work on platforms with Itanium and ARM processors, but due to the unavailability of these systems, we could not make sure of this.
The application is broadcast in PE64 (Portable Executable 64-bit) mode. Some outdated EFI implementations (for example, the
Intel EFI Version 1.10.14.59 Sample Implementation emulator running from a bootable diskette) are not compatible with this application format. This is reflected in the incorrect interpretation of the table of relocatable elements used when configuring the module to load addresses. One of the solutions is to perform the setup programmatically.
Since the FASM translator does not support the EFI Byte Code, to ensure effective programming at the EBC assembler level in the FASM environment, we have to do the following:
- Write a small service utility to automate the rewriting of the Machine Type field in the UEFI application header with the module checksum recalculated.
- Prepare a set of macros for using assembler EBC mnemonics in the FASM environment.
Information sources