EFI Byte Code and memory operations
As you know, the use of virtual machines, whose work is based on a programmatic interpretation of the code, allows you to create universal applications that run on various hardware platforms without recompilation. EFI Byte Code technology is a typical example of a successful application of this approach. But with all its advantages, there is an obvious drawback - a software-implemented processor is much slower than a hardware one. In this article, we consider a method that allows you to level the performance drop of EBC-programs by the example of operations of filling a memory block with a constant and copying the contents of a memory block. Moreover, we are not talking about the use of "inserts" of the native code of the central processor, since this discredits the very idea of cross-platform .
So, let’s imagine that our application needs to fill out the given memory areas with a given constant, as well as copy blocks. Moreover, the arrays are large enough and the performance of this operation is critical for the performance of the application as a whole. Using EBC instructions to process blocks will lead to a loss of performance, and the "insertion" of native code means the loss of cross-platform. How to be
The developers of the UEFI specification have provided an elegant solution for this type of problem. The EFI Boot Services feature set provides procedures for filling the memory block with the SetMem () constant and copying the CopyMem () memory block . Recall UEFI API service procedures are divided into EFI Boot Services and EFI Runtime Services . The former are available only during the OS loading phase, the latter are available during the entire operating time of the OS.
Fig 1 . Description of the parameters of the SetMem () function in the UEFI Specification version 2.4 document Errata A.
Buffer - the base address of the block;
Size - block size;
Value - data to fill the block.
The function fills the memory block with the address equal to the value Buffer, the size in bytes equal to the value Size.
Fig 2 . Description of the CopyMem () function parameters in the UEFI Specification version 2.4 Errata A
Destination document - the base address of the receiving block;
Source - the base address of the source block;
Length - the length for the transfer operation, in bytes.
The function copies the source block of length size located at Source address to the destination block located at Destination address.
Figure 3 shows a listing of an EBC program that fills a 32-byte block with a constant of 11h. Consider its implementation. The base address of the block is written to the register R7, R6 is the length of the block, R5 is the data for writing. After that, a stack frame is created that is used to pass parameters to the called routine. Then, from the EFI System Table, the address of the child EFI Boot Services Table is read , in which, in turn, at number 42 is a pointer to call the SetMem () function . The CALL32EXA instruction is used to lock between the EBC program and the called UEFI firmware procedure. After the subroutine has completed, the stack frame is eliminated.
In this and the following examples, _Primary_Memory_Base and_EFI_Table are offsets used to address variables that store the base address of the memory block used by the program and the base address of the EFI System Table root system table passed to the application at startup, respectively.
Let us recall one feature of constructing UEFI system tables that is essential for ensuring cross-platform. 32-bit UEFI implementations use pointers 4 bytes in size, 64-bit implementations use 8 bytes in size. The table header field is always 24 bytes in size. Therefore, the instructions addressing the UEFI system tables operate on two terms when calculating the address: the entry number and the header size. This allows the EBC virtual machine to correctly calculate the address of the required element, regardless of the native processor capacity, which determines the size of the elements.
Fig 3 . An example of a procedure for filling a block with a constant using the SetMem () function. EBC assembler instructions are used
Figure 4 . The result of the procedure of filling the block with a constant using the SetMem () function. To view, use the Intel EBC Debugger. In this example, the base address of the rendered block is 6C40000h, length 80h = 128 bytes. A constant of 11h is filled in a 32-byte block
Figure 5 shows a listing of an EBC program that copies a 32-byte block. Consider its implementation. The base address of the source block is written to register R7, R6 is the base address of the destination block, R5 is the length of the blocks. After that, a stack frame is created that is used to pass parameters to the called routine. Then, from the EFI System Table, the address of the child EFI Boot Services Table is read, which, in turn, is located at number 41 for a call to the CopyMem () function. The CALL32EXA instruction is used to lock between the EBC program and the called UEFI firmware procedure. After the subroutine has completed, the stack frame is eliminated.
Fig 5 . An example of a procedure for copying a block using the CopyMem () function. EBC assembler instructions are used.
Fig 6 . The result of the copy block procedure using the CopyMem () function. To view, use the Intel EBC Debugger. In this example, the base address of the rendered block is 6C40000h, length 80h = 128 bytes. The source block located at addresses 6C40000h-6C4001Fh is copied to the destination block at addresses 6C40030h-6C4004Fh
The implementation, as part of the UEFI firmware, of the functions associated with the primitive processing of large arrays, theoretically, allows you to optimize the performance of these operations for the particular platform. A significant increase in performance can be achieved using 128 or 256-bit SSE instructions of the x86 central processor, using various DMA coprocessors, as well as hardware implementation using the memory controller. How efficiently platform developers use this potential will be shown by our further research, but even when using classic 32-bit x86 instructions using UEFI firmware procedures, an EBC application without losing cross-platform performance and running on a software processor gets the hardware performance at its disposal the processor.
Formulation of the problem
So, let’s imagine that our application needs to fill out the given memory areas with a given constant, as well as copy blocks. Moreover, the arrays are large enough and the performance of this operation is critical for the performance of the application as a whole. Using EBC instructions to process blocks will lead to a loss of performance, and the "insertion" of native code means the loss of cross-platform. How to be
Solution exists
The developers of the UEFI specification have provided an elegant solution for this type of problem. The EFI Boot Services feature set provides procedures for filling the memory block with the SetMem () constant and copying the CopyMem () memory block . Recall UEFI API service procedures are divided into EFI Boot Services and EFI Runtime Services . The former are available only during the OS loading phase, the latter are available during the entire operating time of the OS.
Fig 1 . Description of the parameters of the SetMem () function in the UEFI Specification version 2.4 document Errata A.
Buffer - the base address of the block;
Size - block size;
Value - data to fill the block.
The function fills the memory block with the address equal to the value Buffer, the size in bytes equal to the value Size.
Fig 2 . Description of the CopyMem () function parameters in the UEFI Specification version 2.4 Errata A
Destination document - the base address of the receiving block;
Source - the base address of the source block;
Length - the length for the transfer operation, in bytes.
The function copies the source block of length size located at Source address to the destination block located at Destination address.
An example of filling a block with a constant
Figure 3 shows a listing of an EBC program that fills a 32-byte block with a constant of 11h. Consider its implementation. The base address of the block is written to the register R7, R6 is the length of the block, R5 is the data for writing. After that, a stack frame is created that is used to pass parameters to the called routine. Then, from the EFI System Table, the address of the child EFI Boot Services Table is read , in which, in turn, at number 42 is a pointer to call the SetMem () function . The CALL32EXA instruction is used to lock between the EBC program and the called UEFI firmware procedure. After the subroutine has completed, the stack frame is eliminated.
In this and the following examples, _Primary_Memory_Base and_EFI_Table are offsets used to address variables that store the base address of the memory block used by the program and the base address of the EFI System Table root system table passed to the application at startup, respectively.
Let us recall one feature of constructing UEFI system tables that is essential for ensuring cross-platform. 32-bit UEFI implementations use pointers 4 bytes in size, 64-bit implementations use 8 bytes in size. The table header field is always 24 bytes in size. Therefore, the instructions addressing the UEFI system tables operate on two terms when calculating the address: the entry number and the header size. This allows the EBC virtual machine to correctly calculate the address of the required element, regardless of the native processor capacity, which determines the size of the elements.
Fig 3 . An example of a procedure for filling a block with a constant using the SetMem () function. EBC assembler instructions are used
Figure 4 . The result of the procedure of filling the block with a constant using the SetMem () function. To view, use the Intel EBC Debugger. In this example, the base address of the rendered block is 6C40000h, length 80h = 128 bytes. A constant of 11h is filled in a 32-byte block
Block Copy Example
Figure 5 shows a listing of an EBC program that copies a 32-byte block. Consider its implementation. The base address of the source block is written to register R7, R6 is the base address of the destination block, R5 is the length of the blocks. After that, a stack frame is created that is used to pass parameters to the called routine. Then, from the EFI System Table, the address of the child EFI Boot Services Table is read, which, in turn, is located at number 41 for a call to the CopyMem () function. The CALL32EXA instruction is used to lock between the EBC program and the called UEFI firmware procedure. After the subroutine has completed, the stack frame is eliminated.
Fig 5 . An example of a procedure for copying a block using the CopyMem () function. EBC assembler instructions are used.
Fig 6 . The result of the copy block procedure using the CopyMem () function. To view, use the Intel EBC Debugger. In this example, the base address of the rendered block is 6C40000h, length 80h = 128 bytes. The source block located at addresses 6C40000h-6C4001Fh is copied to the destination block at addresses 6C40030h-6C4004Fh
Summary
The implementation, as part of the UEFI firmware, of the functions associated with the primitive processing of large arrays, theoretically, allows you to optimize the performance of these operations for the particular platform. A significant increase in performance can be achieved using 128 or 256-bit SSE instructions of the x86 central processor, using various DMA coprocessors, as well as hardware implementation using the memory controller. How efficiently platform developers use this potential will be shown by our further research, but even when using classic 32-bit x86 instructions using UEFI firmware procedures, an EBC application without losing cross-platform performance and running on a software processor gets the hardware performance at its disposal the processor.