📜 ⬆️ ⬇️

Theory and Practice EFI Byte Code

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 .

Machine Type

Figure 1 . Correction of the Machine Type field in the application header

After that, you can run the application.



Figure 2 . Application Result

The application can also be run under the Intel EBC Debugger.

load   EBC-

Figure 3 . Loading the debugger with the load command and running the EBC application under the Intel EBC Debugger debugger

Summary


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:

  1. Write a small service utility to automate the rewriting of the Machine Type field in the UEFI application header with the module checksum recalculated.
  2. Prepare a set of macros for using assembler EBC mnemonics in the FASM environment.


Information sources


Source: https://habr.com/ru/post/201954/


All Articles