diff --git a/misc/efi-stub/Makefile b/misc/efi-stub/Makefile new file mode 100644 index 000000000..48adf2902 --- /dev/null +++ b/misc/efi-stub/Makefile @@ -0,0 +1,96 @@ +# +# Copyright (c) 2011 - 2021, Intel Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +HV_OBJDIR:=build +HV_SRC:=../../hypervisor +C_SRCS = boot.c pe.c malloc.c container.c +ACRN_OBJS := $(patsubst %.c,$(EFI_OBJDIR)/%.o,$(C_SRCS)) +INCLUDE_PATH += $(INCDIR)/efi +INCLUDE_PATH += $(HV_OBJDIR)/include +INCLUDE_PATH += $(HV_SRC)/include/public +INCLUDE_PATH += $(HV_SRC)/include/lib + +BOARD ?= tgl-rvp +SCENARIO ?= hybrid_rt + +OBJCOPY=objcopy + +ARCH := x86_64 +FORMAT := efi-app-x86-64 + +# Different Linux distributions have the 'gnu-efi' package install +# its tools and libraries in different folders. The next couple of +# variables will determine and set the right path for both the +# tools $(GNUEFI_DIR) and libraries $(LIBDIR) +GNUEFI_DIR := $(shell find $(SYSROOT)/usr/lib* -name elf_$(ARCH)_efi.lds -type f | xargs dirname) +LIBDIR := $(subst gnuefi,,$(GNUEFI_DIR)) +CRT0 := $(GNUEFI_DIR)/crt0-efi-$(ARCH).o +LDSCRIPT := $(GNUEFI_DIR)/elf_$(ARCH)_efi.lds + +INCDIR := $(SYSROOT)/usr/include + +CFLAGS=-I. -I.. -I$(INCDIR)/efi -I$(INCDIR)/efi/$(ARCH) \ + -DEFI_FUNCTION_WRAPPER -fPIC -fshort-wchar -ffreestanding \ + -Wall -I../fs/ -D$(ARCH) -O2 \ + -include config.h + +CFLAGS += -mno-mmx -mno-sse -mno-sse2 -mno-80387 -mno-fp-ret-in-387 + +CFLAGS += -fno-delete-null-pointer-checks -fwrapv -mno-red-zone + +LDFLAGS=-T $(LDSCRIPT) -Bsymbolic -shared -nostdlib -znocombreloc \ + -L$(LIBDIR) $(CRT0) +BOOT=$(EFI_OBJDIR)/boot.efi + +all: $(BOOT) + +$(BOOT): $(EFI_OBJDIR)/boot.so + +$(EFI_OBJDIR)/boot.so: $(ACRN_OBJS) + $(LD) $(LDFLAGS) -o $@ $^ -lgnuefi -lefi $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +clean: + rm -f $(BOOT) $(EFI_OBJDIR)/boot.so $(ACRN_OBJS) + +-include $(ACRN_OBJS:.o=.d) + +$(EFI_OBJDIR)/%.o:%.S + [ ! -e $@ ] && mkdir -p $(dir $@); \ + $(CC) $(CFLAGS) -c -o $@ $< -MMD -MT $@ + +$(EFI_OBJDIR)/%.o: %.c + [ ! -e $@ ] && mkdir -p $(dir $@); \ + $(CC) $(patsubst %, -I%, $(INCLUDE_PATH)) -I. -c $(CFLAGS) $(ARCH_CFLAGS) $< -o $@ -MMD -MT $@ + +%.efi: %.so + $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \ + -j .rela -j .reloc --target=$(FORMAT) $*.so $@ diff --git a/misc/efi-stub/MpService.h b/misc/efi-stub/MpService.h new file mode 100644 index 000000000..c31805328 --- /dev/null +++ b/misc/efi-stub/MpService.h @@ -0,0 +1,633 @@ +/** @file + When installed, the MP Services Protocol produces a collection of services + that are needed for MP management. + + The MP Services Protocol provides a generalized way of performing following tasks: + - Retrieving information of multi-processor environment and MP-related status of + specific processors. + - Dispatching user-provided function to APs. + - Maintain MP-related processor status. + + The MP Services Protocol must be produced on any system with more than one logical + processor. + + The Protocol is available only during boot time. + + MP Services Protocol is hardware-independent. Most of the logic of this protocol + is architecturally neutral. It abstracts the multi-processor environment and + status of processors, and provides interfaces to retrieve information, maintain, + and dispatch. + + MP Services Protocol may be consumed by ACPI module. The ACPI module may use this + protocol to retrieve data that are needed for an MP platform and report them to OS. + MP Services Protocol may also be used to program and configure processors, such + as MTRR synchronization for memory space attributes setting in DXE Services. + MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot + by taking advantage of the processing capabilities of the APs, for example, using + APs to help test system memory in parallel with other device initialization. + Diagnostics applications may also use this protocol for multi-processor. + +Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This Protocol is defined in the UEFI Platform Initialization Specification 1.2, + Volume 2:Driver Execution Environment Core Interface. + +**/ + +#ifndef _MP_SERVICE_PROTOCOL_H_ +#define _MP_SERVICE_PROTOCOL_H_ + +typedef VOID(EFIAPI * EFI_AP_PROCEDURE )(IN OUT VOID *Buffer); +/// +/// Global ID for the EFI_MP_SERVICES_PROTOCOL. +/// +#define EFI_MP_SERVICES_PROTOCOL_GUID \ + { \ + 0x3fdda605, 0xa76e, 0x4f46, {0xad, 0x29, 0x12, 0xf4, 0x53, 0x1b, 0x3d, 0x08} \ + } + +/// +/// Forward declaration for the EFI_MP_SERVICES_PROTOCOL. +/// +typedef struct _EFI_MP_SERVICES_PROTOCOL EFI_MP_SERVICES_PROTOCOL; + +/// +/// Terminator for a list of failed CPUs returned by StartAllAPs(). +/// +#define END_OF_CPU_LIST 0xffffffff + +/// +/// This bit is used in the StatusFlag field of EFI_PROCESSOR_INFORMATION and +/// indicates whether the processor is playing the role of BSP. If the bit is 1, +/// then the processor is BSP. Otherwise, it is AP. +/// +#define PROCESSOR_AS_BSP_BIT 0x00000001 + +/// +/// This bit is used in the StatusFlag field of EFI_PROCESSOR_INFORMATION and +/// indicates whether the processor is enabled. If the bit is 1, then the +/// processor is enabled. Otherwise, it is disabled. +/// +#define PROCESSOR_ENABLED_BIT 0x00000002 + +/// +/// This bit is used in the StatusFlag field of EFI_PROCESSOR_INFORMATION and +/// indicates whether the processor is healthy. If the bit is 1, then the +/// processor is healthy. Otherwise, some fault has been detected for the processor. +/// +#define PROCESSOR_HEALTH_STATUS_BIT 0x00000004 + +/// +/// Structure that describes the pyhiscal location of a logical CPU. +/// +typedef struct { + /// + /// Zero-based physical package number that identifies the cartridge of the processor. + /// + UINT32 Package; + /// + /// Zero-based physical core number within package of the processor. + /// + UINT32 Core; + /// + /// Zero-based logical thread number within core of the processor. + /// + UINT32 Thread; +} EFI_CPU_PHYSICAL_LOCATION; + +/// +/// Structure that describes information about a logical CPU. +/// +typedef struct { + /// + /// The unique processor ID determined by system hardware. For IA32 and X64, + /// the processor ID is the same as the Local APIC ID. Only the lower 8 bits + /// are used, and higher bits are reserved. For IPF, the lower 16 bits contains + /// id/eid, and higher bits are reserved. + /// + UINT64 ProcessorId; + /// + /// Flags indicating if the processor is BSP or AP, if the processor is enabled + /// or disabled, and if the processor is healthy. Bits 3..31 are reserved and + /// must be 0. + /// + ///
+  /// BSP  ENABLED  HEALTH  Description
+  /// ===  =======  ======  ===================================================
+  ///  0      0       0     Unhealthy Disabled AP.
+  ///  0      0       1     Healthy Disabled AP.
+  ///  0      1       0     Unhealthy Enabled AP.
+  ///  0      1       1     Healthy Enabled AP.
+  ///  1      0       0     Invalid. The BSP can never be in the disabled state.
+  ///  1      0       1     Invalid. The BSP can never be in the disabled state.
+  ///  1      1       0     Unhealthy Enabled BSP.
+  ///  1      1       1     Healthy Enabled BSP.
+  /// 
+ /// + UINT32 StatusFlag; + /// + /// The physical location of the processor, including the physical package number + /// that identifies the cartridge, the physical core number within package, and + /// logical thread number within core. + /// + EFI_CPU_PHYSICAL_LOCATION Location; +} EFI_PROCESSOR_INFORMATION; + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Protocol provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_GET_NUMBER_OF_PROCESSORS)( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_GET_PROCESSOR_INFO)( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking and non-blocking requests. The non-blocking requests use EFI + events so the BSP can detect when the APs have finished. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all + APs finish or TimeoutInMicroSecs expires. Otherwise, execution is in non-blocking + mode, and the BSP returns from this service without waiting for APs. If a + non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled, then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Protocol does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. EFI services and protocols may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + In non-blocking execution mode, BSP is freed to return to the caller and then + proceed to the next task without having to wait for APs. The following + sequence needs to occur in a non-blocking execution mode: + + -# The caller that intends to use this MP Services Protocol in non-blocking + mode creates WaitEvent by calling the EFI CreateEvent() service. The caller + invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent + is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests + the function specified by Procedure to be started on all the enabled APs, + and releases the BSP to continue with other tasks. + -# The caller can use the CheckEvent() and WaitForEvent() services to check + the state of the WaitEvent created in step 1. + -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP + Service signals WaitEvent by calling the EFI SignalEvent() function. If + FailedCpuList is not NULL, its content is available when WaitEvent is + signaled. If all APs returned from Procedure prior to the timeout, then + FailedCpuList is set to NULL. If not all APs return from Procedure before + the timeout, then FailedCpuList is filled in with the list of the failed + APs. The buffer is allocated by MP Service Protocol using AllocatePool(). + It is the caller's responsibility to free the buffer with FreePool() service. + -# This invocation of SignalEvent() function informs the caller that invoked + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed + the specified task or a timeout occurred. The contents of FailedCpuList + can be examined to determine which APs did not complete the specified task + prior to the timeout. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until all APs finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on all the enabled + APs, and go on executing immediately. If + all return from Procedure, or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + all APs return from Procedure, then Procedure + on the failed APs is terminated. All enabled + APs are available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, + if all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + The buffer is allocated by MP Service Protocol, + and it's the caller's responsibility to + free the buffer with FreePool() service. + In blocking mode, it is ready for consumption + when the call returns. In non-blocking mode, + it is ready when WaitEvent is signaled. The + list of failed CPU is terminated by + END_OF_CPU_LIST. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_STARTUP_ALL_APS)( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to either wait for the completion + of the AP or just proceed with the next task by using the EFI event mechanism. + See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking + execution support. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. If WaitEvent + is NULL, execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode. + BSP proceeds to the next task without waiting for the AP. If a non-blocking mode + is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, + then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and + EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL + instance. + @param[in] Procedure A pointer to the function to be run on the + designated AP of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The range is + from 0 to the total number of logical + processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. If it is NULL, then execute in + blocking mode. BSP waits until this AP finish + or TimeoutInMicroSeconds expires. If it's + not NULL, then execute in non-blocking mode. + BSP requests the function specified by + Procedure to be started on this AP, + and go on executing immediately. If this AP + return from Procedure or TimeoutInMicroSeconds + expires, this event is signaled. The BSP + can use the CheckEvent() or WaitForEvent() + services to check the state of event. Type + EFI_EVENT is defined in CreateEvent() in + the Unified Extensible Firmware Interface + Specification. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + this AP to finish this Procedure, either for + blocking or non-blocking mode. Zero means + infinity. If the timeout expires before + this AP returns from Procedure, then Procedure + on the AP is terminated. The + AP is available for next function assigned + by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + If the timeout expires in blocking mode, + BSP returns EFI_TIMEOUT. If the timeout + expires in non-blocking mode, WaitEvent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure on the + specified AP. + @param[out] Finished If NULL, this parameter is ignored. In + blocking mode, this parameter is ignored. + In non-blocking mode, if AP returns from + Procedure before the timeout expires, its + content is set to TRUE. Otherwise, the + value is set to FALSE. The caller can + determine if the AP returned from Procedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has been + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was + signaled. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_STARTUP_THIS_AP)( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT + is signaled. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP that is to become the new + BSP. The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_SWITCH_BSP)( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. This service may not be supported + after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber The handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only + the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_ENABLEDISABLEAP)( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance. + @param[in] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_MP_SERVICES_WHOAMI)( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ); + +/// +/// When installed, the MP Services Protocol produces a collection of services +/// that are needed for MP management. +/// +/// Before the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, the module +/// that produces this protocol is required to place all APs into an idle state +/// whenever the APs are disabled or the APs are not executing code as requested +/// through the StartupAllAPs() or StartupThisAP() services. The idle state of +/// an AP before the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled is +/// implementation dependent. +/// +/// After the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, all the APs +/// must be placed in the OS compatible CPU state as defined by the UEFI +/// Specification. Implementations of this protocol may use the UEFI event +/// EFI_EVENT_GROUP_READY_TO_BOOT to force APs into the OS compatible state as +/// defined by the UEFI Specification. Modules that use this protocol must +/// guarantee that all non-blocking mode requests on all APs have been completed +/// before the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled. Since the +/// order that event notification functions in the same event group are executed +/// is not deterministic, an event of type EFI_EVENT_GROUP_READY_TO_BOOT cannot +/// be used to guarantee that APs have completed their non-blocking mode requests. +/// +/// When the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, the StartAllAPs() +/// and StartupThisAp() services must no longer support non-blocking mode requests. +/// The support for SwitchBSP() and EnableDisableAP() may no longer be supported +/// after this event is signaled. Since UEFI Applications and UEFI OS Loaders +/// execute after the UEFI event EFI_EVENT_GROUP_READY_TO_BOOT is signaled, these +/// UEFI images must be aware that the functionality of this protocol may be reduced. +/// +struct _EFI_MP_SERVICES_PROTOCOL { + EFI_MP_SERVICES_GET_NUMBER_OF_PROCESSORS GetNumberOfProcessors; + EFI_MP_SERVICES_GET_PROCESSOR_INFO GetProcessorInfo; + EFI_MP_SERVICES_STARTUP_ALL_APS StartupAllAPs; + EFI_MP_SERVICES_STARTUP_THIS_AP StartupThisAP; + EFI_MP_SERVICES_SWITCH_BSP SwitchBSP; + EFI_MP_SERVICES_ENABLEDISABLEAP EnableDisableAP; + EFI_MP_SERVICES_WHOAMI WhoAmI; +}; + +extern EFI_GUID gEfiMpServiceProtocolGuid; + +#endif diff --git a/misc/efi-stub/boot.c b/misc/efi-stub/boot.c new file mode 100644 index 000000000..b587e168c --- /dev/null +++ b/misc/efi-stub/boot.c @@ -0,0 +1,623 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "efilinux.h" +#include "stdlib.h" +#include "boot.h" +#include "acrn_common.h" +#include "MpService.h" +#include "container.h" + +EFI_SYSTEM_TABLE *sys_table; +EFI_BOOT_SERVICES *boot; +HV_LOADER hvld; + +static EFI_STATUS +get_efi_memmap(struct efi_memmap_info *mi, int size_only) +{ + UINTN map_size, map_key; + UINT32 desc_version; + UINTN desc_size; + EFI_MEMORY_DESCRIPTOR *map_buf; + EFI_STATUS err = EFI_SUCCESS; + + /* We're just interested in the map's size for now */ + map_size = 0; + err = get_memory_map(&map_size, NULL, NULL, &desc_size, NULL); + if (err != EFI_SUCCESS && err != EFI_BUFFER_TOO_SMALL) + goto out; + + if (size_only) { + mi->map_size = map_size; + mi->desc_size = desc_size; + return err; + } + +again: + err = allocate_pool(EfiLoaderData, map_size, (void **) &map_buf); + if (err != EFI_SUCCESS) + goto out; + + /* + * Remember! We've already allocated map_buf with emalloc (and + * 'map_size' contains its size) which means that it should be + * positioned below our allocation for the kernel. Use that + * space for the memory map. + */ + err = get_memory_map(&map_size, map_buf, &map_key, + &desc_size, &desc_version); + if (err != EFI_SUCCESS) { + if (err == EFI_BUFFER_TOO_SMALL) { + /* + * Argh! The buffer that we allocated further + * up wasn't large enough which means we need + * to allocate them again, but this time + * larger. 'map_size' has been updated by the + * call to memory_map(). + */ + free_pool(map_buf); + goto again; + } + goto out; + } + + mi->map_size = map_size; + mi->map_key = map_key; + mi->desc_version = desc_version; + mi->desc_size = desc_size; + mi->mmap = map_buf; + +out: + return err; +} + +static EFI_STATUS +terminate_boot_services(EFI_HANDLE image, struct efi_memmap_info *mmap_info) +{ + EFI_STATUS err = EFI_SUCCESS; + + err = exit_boot_services(image, mmap_info->map_key); + if (err != EFI_SUCCESS) { + if (err == EFI_INVALID_PARAMETER) { + /* + * Incorrect map key: memory map changed during the call of get_memory_map + * and exit_boot_services. + * We must call get_memory_map and exit_boot_services one more time. + * We can't allocate nor free pool since exit_boot_services has already been called. + * Original memory pool should be sufficient and this call is expected to succeed. + */ + err = get_memory_map(&mmap_info->map_size, mmap_info->mmap, + &mmap_info->map_key, &mmap_info->desc_size, &mmap_info->desc_version); + if (err != EFI_SUCCESS) + goto out; + + err = exit_boot_services(image, mmap_info->map_key); + if (err != EFI_SUCCESS) + goto out; + } + } + +out: + return err; +} + +static inline void hv_jump(EFI_PHYSICAL_ADDRESS hv_start, + struct multiboot_info *mbi) +{ + hv_func hf; + + /* The 64-bit entry of acrn hypervisor is 0x1200 from the start + * address of hv image. + */ + hf = (hv_func)(hv_start + 0x1200); + + asm volatile ("cli"); + + /* jump to acrn hypervisor */ + hf(MB_INFO_MAGIC, mbi); +} + +static EFI_STATUS +fill_e820(HV_LOADER hvld, struct efi_memmap_info *mmap_info, + struct multiboot_mmap *mmap, int32_t *e820_count) +{ + EFI_STATUS err = EFI_SUCCESS; + uint32_t mmap_entry_count = mmap_info->map_size / mmap_info->desc_size; + int32_t i, j; + + /* + * Convert the EFI memory map to E820. + */ + for (i = 0, j = 0; i < mmap_entry_count && j < MBOOT_MMAP_NUMS - 1; i++) { + EFI_MEMORY_DESCRIPTOR *d; + uint32_t e820_type = 0; + + d = (EFI_MEMORY_DESCRIPTOR *)((uint64_t)mmap_info->mmap + \ + (i * mmap_info->desc_size)); + switch(d->Type) { + case EfiReservedMemoryType: + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiMemoryMappedIO: + case EfiMemoryMappedIOPortSpace: + case EfiPalCode: + e820_type = E820_RESERVED; + break; + + case EfiUnusableMemory: + e820_type = E820_UNUSABLE; + break; + + case EfiACPIReclaimMemory: + e820_type = E820_ACPI; + break; + + case EfiLoaderCode: + case EfiLoaderData: + case EfiBootServicesCode: + case EfiBootServicesData: + case EfiConventionalMemory: + e820_type = E820_RAM; + break; + + case EfiACPIMemoryNVS: + e820_type = E820_NVS; + break; + + default: + continue; + } + + if ((j != 0) && mmap[j-1].mm_type == e820_type && + (mmap[j-1].mm_base_addr + mmap[j-1].mm_length) + == d->PhysicalStart) { + mmap[j-1].mm_length += d->NumberOfPages << EFI_PAGE_SHIFT; + } else { + mmap[j].mm_base_addr = d->PhysicalStart; + mmap[j].mm_length = d->NumberOfPages << EFI_PAGE_SHIFT; + mmap[j].mm_type = e820_type; + j++; + } + } + + /* + * if we haven't gone through all the mmap table entries, + * there must be a memory overwrite if we continue, + * so just abort anyway. + */ + if (i < mmap_entry_count) { + Print(L": bios provides %d mmap entries which is beyond limitation[%d]\n", + mmap_entry_count, MBOOT_MMAP_NUMS-1); + err = EFI_INVALID_PARAMETER; + goto out; + } + + /* switch hv memory region(0x20000000 ~ 0x22000000) to + * available RAM in e820 table + */ + mmap[j].mm_base_addr = hvld->get_hv_hpa(hvld); + mmap[j].mm_length = CONFIG_HV_RAM_SIZE; + mmap[j].mm_type = E820_RAM; + j++; + + mmap[j].mm_base_addr = hvld->get_mod_hpa(hvld); + mmap[j].mm_length = hvld->get_total_modsize(hvld); + mmap[j].mm_type = E820_RAM; + j++; + + *e820_count = j; +out: + return err; +} + + +EFI_STATUS construct_mbi(HV_LOADER hvld, struct multiboot_info **mbinfo, struct efi_memmap_info *mmap_info) +{ + EFI_STATUS err = EFI_SUCCESS; + int32_t e820_count = 0; + EFI_PHYSICAL_ADDRESS addr; + struct multiboot_mmap *mmap; + struct multiboot_info *mbi; + char *uefi_boot_loader_name; + static const char loader_name[BOOT_LOADER_NAME_SIZE] = UEFI_BOOT_LOADER_NAME; + + err = allocate_pool(EfiLoaderData, EFI_BOOT_MEM_SIZE, (VOID *)&addr); + if (err != EFI_SUCCESS) { + Print(L"Failed to allocate memory for EFI boot\n"); + goto out; + } + (void)memset((void *)addr, 0x0, EFI_BOOT_MEM_SIZE); + + mmap = MBOOT_MMAP_PTR(addr); + mbi = MBOOT_INFO_PTR(addr); + + uefi_boot_loader_name = BOOT_LOADER_NAME_PTR(addr); + memcpy(uefi_boot_loader_name, loader_name, BOOT_LOADER_NAME_SIZE); + + err = get_efi_memmap(mmap_info, 0); + if (err != EFI_SUCCESS) + goto out; + + err = fill_e820(hvld, mmap_info, mmap, &e820_count); + if (err != EFI_SUCCESS) + goto out; + + /* TODO: Current container library does not handle mbi1 case */ + /* mbi->mi_cmdline = (UINTN)hv_info->cmdline; */ + mbi->mi_mmap_addr = (UINTN)mmap; + mbi->mi_mmap_length = e820_count*sizeof(struct multiboot_mmap); + mbi->mi_flags |= MULTIBOOT_INFO_HAS_MMAP | MULTIBOOT_INFO_HAS_CMDLINE; + + /* Set boot loader name in the multiboot header of UEFI, this name is used by hypervisor; + * The host physical start address of boot loader name is stored in multiboot header. + */ + mbi->mi_flags |= MULTIBOOT_INFO_HAS_LOADER_NAME; + mbi->mi_loader_name = (UINT32)(uint64_t)uefi_boot_loader_name; + + mbi->mi_mods_addr = hvld->get_mod_hpa(hvld); + mbi->mi_mods_count = hvld->get_mod_count(hvld); + mbi->mi_flags |= MULTIBOOT_INFO_HAS_MODS; + + *mbinfo = mbi; +out: + return err; +} + +#ifdef CONFIG_MULTIBOOT2 +static struct acpi_table_rsdp * +search_rsdp() +{ + unsigned i; + struct acpi_table_rsdp *rsdp = NULL; + EFI_CONFIGURATION_TABLE *config_table = sys_table->ConfigurationTable; + + for (i = 0; i < sys_table->NumberOfTableEntries; i++) { + EFI_GUID acpi_20_table_guid = ACPI_20_TABLE_GUID; + EFI_GUID acpi_table_guid = ACPI_TABLE_GUID; + + if (CompareGuid(&acpi_20_table_guid, + &config_table->VendorGuid) == 0) { + rsdp = config_table->VendorTable; + break; + } + + if (CompareGuid(&acpi_table_guid, + &config_table->VendorGuid) == 0) + rsdp = config_table->VendorTable; + + config_table++; + } + + return rsdp; +} + +static uint32_t +get_mbi2_size(HV_LOADER hvld, struct efi_memmap_info *mmap_info, uint32_t rsdp_length) +{ + uint32_t mmap_entry_count = mmap_info->map_size / mmap_info->desc_size; + + return 2 * sizeof(uint32_t) \ + /* Boot command line */ + + (sizeof(struct multiboot2_tag_string) + \ + ALIGN_UP(hvld->get_boot_cmdsize(hvld), MULTIBOOT2_TAG_ALIGN)) \ + + /* Boot loader name */ + + (sizeof(struct multiboot2_tag_string) + \ + ALIGN_UP(BOOT_LOADER_NAME_SIZE, MULTIBOOT2_TAG_ALIGN)) \ + + /* Modules */ + + (hvld->get_mod_count(hvld) * sizeof(struct multiboot2_tag_module) + \ + hvld->get_total_modcmdsize(hvld)) \ + + /* Memory Map */ + + ALIGN_UP((sizeof(struct multiboot2_tag_mmap) + \ + mmap_entry_count * sizeof(struct multiboot2_mmap_entry)), MULTIBOOT2_TAG_ALIGN) \ + + /* ACPI new */ + + ALIGN_UP(sizeof(struct multiboot2_tag_new_acpi) + \ + rsdp_length, MULTIBOOT2_TAG_ALIGN) \ + + /* EFI64 system table */ + + ALIGN_UP(sizeof(struct multiboot2_tag_efi64), MULTIBOOT2_TAG_ALIGN) \ + + /* EFI memmap: Add an extra page since UEFI can alter the memory map */ + + ALIGN_UP(sizeof(struct multiboot2_tag_efi_mmap) + \ + ALIGN_UP(mmap_info->map_size + 0x1000, 0x1000), MULTIBOOT2_TAG_ALIGN) \ + + /* END */ + + sizeof(struct multiboot2_tag); +} + +EFI_STATUS +construct_mbi2(struct hv_loader *hvld, void **mbi_addr, struct efi_memmap_info *mmap_info) +{ + uint64_t *mbistart; + uint64_t *p; + uint32_t mbi2_size; + struct multiboot_mmap *mmap; + struct acpi_table_rsdp *rsdp; + EFI_STATUS err; + + rsdp = search_rsdp(); + if (!rsdp) + return EFI_NOT_FOUND; + + /* Get size only for mbi size calculation */ + err = get_efi_memmap(mmap_info, 1); + if (err != EFI_SUCCESS && err != EFI_BUFFER_TOO_SMALL) + return err; + + mbi2_size = get_mbi2_size(hvld, mmap_info, rsdp->length); + + /* per UEFI spec v2.9: This allocation is guaranteed to be 8-bytes aligned */ + err = allocate_pool(EfiLoaderData, mbi2_size, (void **)&mbistart); + if (err != EFI_SUCCESS) + goto out; + + memset(mbistart, 0x0, mbi2_size); + + /* Allocate temp buffer to hold memory map */ + err = allocate_pool(EfiLoaderData, + (mmap_info->map_size / mmap_info->desc_size) * sizeof(struct multiboot_mmap), + (void **)&mmap); + if (err != EFI_SUCCESS) + goto out; + + /* + * Get full memory map again. + * We have just allocated memory and the mmap_info will be different. + */ + err = get_efi_memmap(mmap_info, 0); + if (err != EFI_SUCCESS) + goto out; + + /* total_size and reserved */ + p = mbistart; + p += (2 * sizeof(uint32_t)) / sizeof(uint64_t); + + /* Boot command line */ + { + struct multiboot2_tag_string *tag = (struct multiboot2_tag_string *)p; + (void)hvld->fill_bootcmd_tag(hvld, tag); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* Boot loader name */ + { + struct multiboot2_tag_string *tag = (struct multiboot2_tag_string *)p; + tag->type = MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME; + tag->size = sizeof(struct multiboot2_tag_string) + BOOT_LOADER_NAME_SIZE; + memcpy(tag->string, UEFI_BOOT_LOADER_NAME, BOOT_LOADER_NAME_SIZE); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* Modules */ + { + unsigned i; + uint32_t mod_count = hvld->get_mod_count(hvld); + for (i = 0; i < mod_count; i++) { + struct multiboot2_tag_module *tag = (struct multiboot2_tag_module *)p; + (void)hvld->fill_module_tag(hvld, tag, i); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + } + + /* Memory map */ + { + unsigned i; + struct multiboot2_tag_mmap *tag = (struct multiboot2_tag_mmap *)p; + struct multiboot2_mmap_entry *e; + int32_t e820_count = 0; + + err = fill_e820(hvld, mmap_info, mmap, &e820_count); + if (err != EFI_SUCCESS) + goto out; + + tag->type = MULTIBOOT2_TAG_TYPE_MMAP; + tag->size = sizeof(struct multiboot2_tag_mmap) + sizeof(struct multiboot2_mmap_entry) * e820_count; + tag->entry_size = sizeof(struct multiboot2_mmap_entry); + tag->entry_version = 0; + + for (i = 0, e = (struct multiboot2_mmap_entry *)tag->entries; i < e820_count; i++) { + e->addr = mmap[i].mm_base_addr; + e->len = mmap[i].mm_length; + e->type = mmap[i].mm_type; + e->zero = 0; + e = (struct multiboot2_mmap_entry *)((char *)e + sizeof(struct multiboot2_mmap_entry)); + } + + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* ACPI new */ + { + struct multiboot2_tag_new_acpi *tag = (struct multiboot2_tag_new_acpi *)p; + tag->type = MULTIBOOT2_TAG_TYPE_ACPI_NEW; + tag->size = sizeof(struct multiboot2_tag_new_acpi) + rsdp->length; + memcpy((char *)tag->rsdp, (char *)rsdp, rsdp->length); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* EFI64 system table */ + { + struct multiboot2_tag_efi64 *tag = (struct multiboot2_tag_efi64 *)p; + tag->type = MULTIBOOT2_TAG_TYPE_EFI64; + tag->size = sizeof(struct multiboot2_tag_efi64); + tag->pointer = (uint64_t)sys_table; + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* EFI memory map */ + { + struct multiboot2_tag_efi_mmap *tag = (struct multiboot2_tag_efi_mmap *)p; + tag->type = MULTIBOOT2_TAG_TYPE_EFI_MMAP; + tag->size = sizeof(struct multiboot2_tag_efi_mmap) + mmap_info->map_size; + tag->descr_size = mmap_info->desc_size; + tag->descr_vers = mmap_info->desc_version; + memcpy((char *)tag->efi_mmap, (char *)mmap_info->mmap, mmap_info->map_size); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + /* END */ + { + struct multiboot2_tag *tag = (struct multiboot2_tag *)p; + tag->type = MULTIBOOT2_TAG_TYPE_END; + tag->size = sizeof(struct multiboot2_tag); + p += ALIGN_UP(tag->size, MULTIBOOT2_TAG_ALIGN) / sizeof(uint64_t); + } + + ((uint32_t *)mbistart)[0] = (uint64_t)((char *)p - (char *)mbistart); + ((uint32_t *)mbistart)[1] = 0; + + *mbi_addr = (void *)mbistart; + + return EFI_SUCCESS; + +out: + free_pool(mbistart); + return err; +} +#endif + +static EFI_STATUS +run_acrn(EFI_HANDLE image, HV_LOADER hvld) +{ + EFI_STATUS err; + struct efi_memmap_info memmapinfo; + struct multiboot_info *mbi; + +#ifdef CONFIG_MULTIBOOT2 + /* MB2 has no fixed mbinfo layout. The mbi as output will + * NOT conform to the layout of struct multiboot_info. + */ + err = construct_mbi2(hvld, (void **)&mbi, &memmapinfo); +#else + err = construct_mbi(hvld, &mbi, &memmapinfo); +#endif + if (err != EFI_SUCCESS) + goto out; + + err = terminate_boot_services(image, &memmapinfo); + if (err != EFI_SUCCESS) + goto out; + + hv_jump(hvld->get_hv_hpa(hvld), mbi); + + /* Not reached on success */ +out: + return err; +} + +/** + * efi_main - The entry point for the OS loader image. + * @image: firmware-allocated handle that identifies the image + * @sys_table: EFI system table + */ +EFI_STATUS +efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table) +{ + WCHAR *error_buf; + EFI_STATUS err; + EFI_LOADED_IMAGE *info; + + INTN index; + + InitializeLib(image, _table); + sys_table = _table; + boot = sys_table->BootServices; + + if (CheckCrc(sys_table->Hdr.HeaderSize, &sys_table->Hdr) != TRUE) + return EFI_LOAD_ERROR; + + err = handle_protocol(image, &LoadedImageProtocol, (void **)&info); + if (err != EFI_SUCCESS) + goto failed; + + /* + * Load hypervisor boot image handler. Currently Slim Bootloader + * compatible embedded container format is supported. File system + * mode to come future. + */ + err = container_init(info, &hvld); + if (err != EFI_SUCCESS) { + Print(L"Unable to init container library %r ", err); + goto failed; + } + + err = hvld->load_boot_image(hvld); + if (err != EFI_SUCCESS) { + Print(L"Unable to load ACRNHV Image %r ", err); + goto failed; + } + + err = hvld->load_modules(hvld); + if (err != EFI_SUCCESS) { + Print(L"Unable to load VM modules %r ", err); + goto failed; + } + + err = run_acrn(image, hvld); + if (err != EFI_SUCCESS) + goto failed; + + return EFI_SUCCESS; + +failed: + if (hvld) { + hvld->deinit(hvld); + } + + /* + * We need to be careful not to trash 'err' here. If we fail + * to allocate enough memory to hold the error string fallback + * to returning 'err'. + */ + if (allocate_pool(EfiLoaderData, ERROR_STRING_LENGTH, + (void **)&error_buf) != EFI_SUCCESS) { + Print(L"Couldn't allocate pages for error string\n"); + return err; + } + + StatusToString(error_buf, err); + Print(L": %s\n", error_buf); + + /* If we don't wait for user input, (s)he will not see the error message */ + uefi_call_wrapper(sys_table->ConOut->OutputString, 2, sys_table->ConOut, \ + L"\r\n\r\n\r\nHit any key to exit\r\n"); + uefi_call_wrapper(sys_table->BootServices->WaitForEvent, 3, 1, \ + &sys_table->ConIn->WaitForKey, &index); + + return exit(image, err, ERROR_STRING_LENGTH, error_buf); +} diff --git a/misc/efi-stub/boot.h b/misc/efi-stub/boot.h new file mode 100644 index 000000000..43b8fc2a3 --- /dev/null +++ b/misc/efi-stub/boot.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ACRNBOOT_H__ +#define __ACRNBOOT_H__ + +#include "multiboot.h" + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 + +#define ERROR_STRING_LENGTH 32 +#define EFI_LOADER_SIGNATURE "EL64" + +#define ACPI_XSDT_ENTRY_SIZE (sizeof(UINT64)) +#define ACPI_NAME_SIZE 4 +#define ACPI_OEM_ID_SIZE 6 +#define ACPI_OEM_TABLE_ID_SIZE 8 + +#define MSR_IA32_PAT 0x00000277 /* PAT */ +#define MSR_IA32_EFER 0xC0000080 +#define MSR_IA32_FS_BASE 0xC0000100U +#define MSR_IA32_GS_BASE 0xC0000101 +#define MSR_IA32_SYSENTER_ESP 0x00000175 /* ESP for sysenter */ +#define MSR_IA32_SYSENTER_EIP 0x00000176 /* EIP for sysenter */ + +#define UEFI_BOOT_LOADER_NAME "ACRN UEFI loader" + +#define ALIGN_UP(addr, align) \ + (((addr) + (typeof (addr)) (align) - 1) & ~((typeof (addr)) (align) - 1)) + +/* Read MSR */ +#define CPU_MSR_READ(reg, msr_val_ptr) \ +{ \ + uint32_t msrl, msrh; \ + asm volatile ("rdmsr" : "=a"(msrl), \ + "=d"(msrh) : "c" (reg)); \ + *msr_val_ptr = ((uint64_t)msrh << 32U) | msrl; \ +} + +EFI_STATUS get_pe_section(CHAR8 *base, char *section_name, UINTN section_name_len, UINTN *vaddr, UINTN *size); +typedef void(*hv_func)(int32_t, struct multiboot_info*); + +/* + * We allocate memory for the following struct together with hyperivosr itself + * memory allocation during boot. + */ +#define MBOOT_MMAP_NUMS 256 +#define MBOOT_MMAP_SIZE (sizeof(struct multiboot_mmap) * MBOOT_MMAP_NUMS) +#define MBOOT_INFO_SIZE (sizeof(struct multiboot_info)) +#define MBOOT_MODS_NUMS 4 +#define MBOOT_MODS_SIZE (sizeof(struct multiboot_module) * MBOOT_MODS_NUMS) +#define BOOT_LOADER_NAME_SIZE 17U +#define EFI_BOOT_MEM_SIZE \ + (MBOOT_MMAP_SIZE + MBOOT_INFO_SIZE + MBOOT_MODS_SIZE + BOOT_LOADER_NAME_SIZE) +#define MBOOT_MMAP_PTR(addr) \ + ((struct multiboot_mmap *)((VOID *)(addr))) +#define MBOOT_INFO_PTR(addr) \ + ((struct multiboot_info *)((VOID *)(addr) + MBOOT_MMAP_SIZE)) +#define MBOOT_MODS_PTR(addr) \ + ((struct multiboot_module *)((VOID *)(addr) + MBOOT_MMAP_SIZE + MBOOT_INFO_SIZE)) +#define BOOT_LOADER_NAME_PTR(addr) \ + ((char *)((VOID *)(addr) + MBOOT_MMAP_SIZE + MBOOT_INFO_SIZE + MBOOT_MODS_SIZE)) + +struct efi_memmap_info { + UINTN map_size; + UINTN map_key; + UINT32 desc_version; + UINTN desc_size; + EFI_MEMORY_DESCRIPTOR *mmap; +}; + +struct efi_info { + UINT32 efi_loader_signature; + UINT32 efi_systab; + UINT32 efi_memdesc_size; + UINT32 efi_memdesc_version; + UINT32 efi_memmap; + UINT32 efi_memmap_size; + UINT32 efi_systab_hi; + UINT32 efi_memmap_hi; +}; + +struct e820_entry { + UINT64 addr; /* start of memory segment */ + UINT64 size; /* size of memory segment */ + UINT32 type; /* type of memory segment */ +} __attribute__((packed)); + +struct acpi_table_rsdp { + /* ACPI signature, contains "RSD PTR " */ + char signature[8]; + /* ACPI 1.0 checksum */ + UINT8 checksum; + /* OEM identification */ + char oem_id[ACPI_OEM_ID_SIZE]; + /* Must be (0) for ACPI 1.0 or (2) for ACPI 2.0+ */ + UINT8 revision; + /* 32-bit physical address of the RSDT */ + UINT32 rsdt_physical_address; + /* Table length in bytes, including header (ACPI 2.0+) */ + UINT32 length; + /* 64-bit physical address of the XSDT (ACPI 2.0+) */ + UINT64 xsdt_physical_address; + /* Checksum of entire table (ACPI 2.0+) */ + UINT8 extended_checksum; + /* Reserved, must be zero */ + UINT8 reserved[3]; +}; + +struct acpi_table_header { + /* ASCII table signature */ + char signature[ACPI_NAME_SIZE]; + /* Length of table in bytes, including this header */ + UINT32 length; + /* ACPI Specification minor version number */ + UINT8 revision; + /* To make sum of entire table == 0 */ + UINT8 checksum; + /* ASCII OEM identification */ + char oem_id[ACPI_OEM_ID_SIZE]; + /* ASCII OEM table identification */ + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; + /* OEM revision number */ + UINT32 oem_revision; + /* ASCII ASL compiler vendor ID */ + char asl_compiler_id[ACPI_NAME_SIZE]; + /* ASL compiler version */ + UINT32 asl_compiler_revision; +}; + +/* hypervisor loader operation table */ +typedef struct hv_loader *HV_LOADER; +struct hv_loader { + /* Load ACRN hypervisor image into memory */ + EFI_STATUS (*load_boot_image)(IN HV_LOADER hvld); + /* Load VM Kernels and ACPI Tables into memory */ + EFI_STATUS (*load_modules)(IN HV_LOADER hvld); + + /* Get hypervisor boot command length */ + UINTN (*get_boot_cmdsize)(IN HV_LOADER hvld); + /* Get the number of multiboot2 modules */ + UINTN (*get_mod_count)(IN HV_LOADER hvld); + /* Get the total memory size allocated to load module files */ + UINTN (*get_total_modsize)(IN HV_LOADER hvld); + /* Get the total lengths of the module commands */ + UINTN (*get_total_modcmdsize)(IN HV_LOADER hvld); + + /* Get the start address of the memory region stored ACRN hypervisor image */ + EFI_PHYSICAL_ADDRESS (*get_hv_hpa)(IN HV_LOADER hvld); + /* Get the start address of the memory region stored module files */ + EFI_PHYSICAL_ADDRESS (*get_mod_hpa)(IN HV_LOADER hvld); + + /* Set hypervisor boot command line to multiboot2 tag */ + void (*fill_bootcmd_tag)(IN HV_LOADER hvld, OUT struct multiboot2_tag_string *tag); + /* Set n-th module info to multiboot2 tag */ + void (*fill_module_tag)(IN HV_LOADER hvld, OUT struct multiboot2_tag_module *tag, IN UINTN index); + + /* free up memory allocated by hypervisor loader */ + void (*deinit)(IN HV_LOADER hvld); +}; + +static inline uint64_t +msr_read(uint32_t reg_num) +{ + uint64_t msr_val; + + CPU_MSR_READ(reg_num, &msr_val); + return msr_val; +} + +#endif diff --git a/misc/efi-stub/container.c b/misc/efi-stub/container.c new file mode 100644 index 000000000..212c3cb28 --- /dev/null +++ b/misc/efi-stub/container.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2021, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Library to support ACRN HV booting with Slim Bootloader container + * +*/ + +#include +#include +#include +#include "boot.h" +#include "stdlib.h" +#include "efilinux.h" +#include "multiboot.h" +#include "container.h" + +#define LZH_BOOT_CMD 0u +#define LZH_BOOT_IMG 1u +#define LZH_MOD0_CMD 2u + +typedef struct { + UINT32 Signature; + UINT8 Version; + UINT8 Svn; + UINT16 DataOffset; + UINT32 DataSize; + UINT8 AuthType; + UINT8 ImageType; + UINT8 Flags; + UINT8 Count; +} CONTAINER_HDR; + +typedef struct { + UINT32 Name; + UINT32 Offset; + UINT32 Size; + UINT8 Attribute; + UINT8 Alignment; + UINT8 AuthType; + UINT8 HashSize; + UINT8 HashData[0]; +} COMPONENT_ENTRY; + +typedef struct { + UINT32 Signature; + UINT32 CompressedSize; + UINT32 Size; + UINT16 Version; + UINT8 Svn; + UINT8 Attribute; + UINT8 Data[]; +} LOADER_COMPRESSED_HEADER; + +struct container { + struct hv_loader ops; /* loader operation table */ + + CHAR16 *options; /* uefi boot option passed by efibootmgr -u */ + UINT32 options_size; /* length of UEFI boot option */ + UINTN boot_cmdsize; /* length of boot command to pass hypervisor */ + + EFI_PHYSICAL_ADDRESS hv_hpa; /* start of memory stored hv image */ + EFI_PHYSICAL_ADDRESS mod_hpa; /* start of memory stored module files */ + + UINTN mod_count; /* num of modules */ + UINTN total_modsize; /* memory size allocated to load modules */ + UINTN total_modcmdsize; /* memory size to store module commands */ + + UINTN lzh_count; /* num of files in container */ + LOADER_COMPRESSED_HEADER *lzh_ptr[]; /* cache of each file header in container */ +}; + +/** + * @brief Load acrn.32.out ELF file + * + * @param[in] elf_image ELF image + * @param[out] hv_hpa The physical memory address the relocated hypervisor is stored + * + * @return EFI_SUCCESS(0) on success, non-zero on error + */ +static EFI_STATUS load_acrn_elf(const UINT8 *elf_image, EFI_PHYSICAL_ADDRESS *hv_hpa) +{ + EFI_STATUS err = EFI_SUCCESS; + int i; + + EFI_PHYSICAL_ADDRESS addr = 0u; + + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)(elf_image); + Elf32_Phdr *pbase = (Elf32_Phdr *)(elf_image + ehdr->e_phoff); + Elf32_Phdr *phdr = (Elf32_Phdr *)(elf_image + ehdr->e_phoff); + + /* without relocation enabled, hypervisor binary need to reside in + * fixed memory address starting from CONFIG_HV_RAM_START, make a call + * to emalloc_fixed_addr for that case. With CONFIG_RELOC enabled, + * hypervisor is able to do relocation, the only requirement is that + * it need to reside in memory below 4GB, call emalloc_reserved_mem() + * instead. + * + * Don't relocate hypervisor binary under 256MB, which could be where + * guest Linux kernel boots from, and other usage, e.g. hvlog buffer + */ +#ifdef CONFIG_RELOC + err = emalloc_reserved_aligned(hv_hpa, CONFIG_HV_RAM_SIZE, 2U * MEM_ADDR_1MB, + 256U * MEM_ADDR_1MB, MEM_ADDR_4GB); +#else + err = emalloc_fixed_addr(hv_hpa, CONFIG_HV_RAM_SIZE, CONFIG_HV_RAM_START); +#endif + + if (err != EFI_SUCCESS) { + Print(L"Failed to allocate memory for ACRN HV %r\n", err); + goto out; + } + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = (Elf32_Phdr *)((UINT8 *)pbase + i * ehdr->e_phentsize); + if ((phdr->p_type != PT_LOAD) || (phdr->p_memsz == 0) || (phdr->p_offset == 0)) { + continue; + } + + if (phdr->p_filesz > phdr->p_memsz) { + err = EFI_LOAD_ERROR; + goto out; + } + + addr = (EFI_PHYSICAL_ADDRESS)(*hv_hpa + (phdr->p_paddr - CONFIG_HV_RAM_START)); + memcpy((char *)addr, (const char *)(elf_image + phdr->p_offset), phdr->p_filesz); + + if (phdr->p_memsz > phdr->p_filesz) { + addr = (EFI_PHYSICAL_ADDRESS)(*hv_hpa + (phdr->p_paddr - CONFIG_HV_RAM_START + phdr->p_filesz)); + (void)memset((void *)addr, 0x0, (phdr->p_memsz - phdr->p_filesz)); + } + } + +out: + return err; +} + +/** + * @brief Load hypervisor into memory from a container blob + * + * @param[in] hvld Loader handle + * + * @return EFI_SUCCESS(0) on success, non-zero on error + */ +static EFI_STATUS container_load_boot_image(HV_LOADER hvld) +{ + EFI_STATUS err = EFI_SUCCESS; + struct container *ctr = (struct container *)hvld; + + LOADER_COMPRESSED_HEADER *lzh = NULL; + + /* hv_cmdline.txt: to be copied into memory by the fill_bootcmd_tag operation later */ + lzh = ctr->lzh_ptr[LZH_BOOT_CMD]; + ctr->boot_cmdsize = lzh->Size + StrnLen(ctr->options, ctr->options_size); + + /* acrn.32.out */ + lzh = ctr->lzh_ptr[LZH_BOOT_IMG]; + err = load_acrn_elf((const UINT8 *)lzh->Data, &ctr->hv_hpa); + if (err != EFI_SUCCESS) { + Print(L"Failed to load ACRN HV ELF Image%r\n", err); + goto out; + } +out: + return err; +} + +/** + * @brief Load kernel modules and acpi tables into memory from a container blob + * + * @param[in] hvld Loader handle + * + * @return EFI_SUCCESS(0) on success, non-zero on error + */ + static EFI_STATUS container_load_modules(HV_LOADER hvld) +{ + EFI_STATUS err = EFI_SUCCESS; + struct container *ctr = (struct container *)hvld; + + UINTN i; + + UINT8 * p = NULL; + LOADER_COMPRESSED_HEADER *lzh = NULL; + + /* scan module headers to calculate required memory size to store files */ + for (i = LZH_MOD0_CMD; i < ctr->lzh_count - 1; i++) { + if ((i % 2) == 0) { /* vm0_tag.txt, vm1_tag.txt, acpi_vm0.txt ... */ + ctr->total_modcmdsize += ctr->lzh_ptr[i]->Size; + } else { /* vm0_kernel, vm1_kernel, vm0_acpi.bin ... */ + ctr->total_modsize += ALIGN_UP(ctr->lzh_ptr[i]->Size, EFI_PAGE_SIZE); + } + } + /* exclude hypervisor and SBL signature files. e.g.) + * lzh_count = 9 (hv_cmdline, acrn.32.out, vm0_tag, vm0_kernel, vm1_tag, vm1_kernel, vm0_acpi_tag, vm0_acpi, sig) + * mod_count = 3 (vm0_tag + vm0_kernel, vm1_tag + vm1_kernel, vm0_acpi_tag + vm0_acpi) + */ + ctr->mod_count = (ctr->lzh_count - 3) / 2; + + /* allocate single memory region to store all binary files to avoid mmap fragmentation */ + err = emalloc_reserved_aligned(&(ctr->mod_hpa), ctr->total_modsize, + EFI_PAGE_SIZE, 256U * MEM_ADDR_1MB, MEM_ADDR_4GB); + if (err != EFI_SUCCESS) { + Print(L"Failed to allocate memory for modules %r\n", err); + goto out; + } + + p = (UINT8 *)ctr->mod_hpa; + for (i = LZH_BOOT_IMG + 2; i < ctr->lzh_count - 1; i = i + 2) { + lzh = ctr->lzh_ptr[i]; + memcpy((char *)p, (const char *)lzh->Data, lzh->Size); + p += ALIGN_UP(lzh->Size, EFI_PAGE_SIZE); + } +out: + return err; +} + +/** + * @brief Get hypervisor boot command length + * + * @param[in] hvld Loader handle + * + * @return the length of hypervisor boot command + */ +static UINTN container_get_boot_cmdsize(HV_LOADER hvld) +{ + /* boot_cmd = hv_cmdline.txt in container + extra arg given by the 'efibootmgr -u' option */ + return ((struct container *)hvld)->boot_cmdsize; +} + +/** + * @brief Get the number of multiboot2 modules + * + * @param[in] hvld Loader handle + * + * @return the number of multiboot2 modules + */ +static UINTN container_get_mod_count(HV_LOADER hvld) +{ + return ((struct container *)hvld)->mod_count; +} + +/** + * @brief Get the total memory size allocated to load module files + * + * @param[in] hvld Loader handle + * + * @return the total size of memory allocated to store the module files + */ +static UINTN container_get_total_modsize(HV_LOADER hvld) +{ + return ((struct container *)hvld)->total_modsize; +} + +/** + * @brief Get the total lengths of the module commands + * + * @param[in] hvld Loader handle + * + * @return the total lengths of module command files + */ +static UINTN container_get_total_modcmdsize(HV_LOADER hvld) +{ + return ((struct container *)hvld)->total_modcmdsize; +} + +/** + * @brief Get the start address of the memory region stored ACRN hypervisor image + * + * @param[in] hvld Loader handle + * + * @return the address of hv image + */ +static EFI_PHYSICAL_ADDRESS container_get_hv_hpa(HV_LOADER hvld) +{ + return ((struct container *)hvld)->hv_hpa; +} + +/** + * @brief Get the start address of the memory region stored module files + * + * @param[in] hvld Loader handle + * + * @return the address of modules + */ +static EFI_PHYSICAL_ADDRESS container_get_mod_hpa(HV_LOADER hvld) +{ + return ((struct container *)hvld)->mod_hpa; +} + +/** + * @brief Set hypervisor boot command line to multiboot2 tag + * + * @param[in] hvld Loader handle + * @param[out] tag The buffer to be filled in. It's the caller's responsibility to allocate memory for the buffer + * + * @return None + */ +static void container_fill_bootcmd_tag(HV_LOADER hvld, struct multiboot2_tag_string *tag) +{ + struct container *ctr = (struct container *)hvld; + LOADER_COMPRESSED_HEADER *lzh = ctr->lzh_ptr[LZH_BOOT_CMD]; + UINTN cmdline_size = container_get_boot_cmdsize(hvld); + + UINTN i; + + tag->type = MULTIBOOT2_TAG_TYPE_CMDLINE; + tag->size = sizeof(struct multiboot2_tag_string) + cmdline_size; + + (void)memset((void *)tag->string, 0x0, cmdline_size); + memcpy(tag->string, (const char *)lzh->Data, lzh->Size - 1); + if (ctr->options) { + tag->string[lzh->Size - 1] = ' '; + for (i = lzh->Size; i < cmdline_size; i++) { + /* append the options to the boot command line */ + tag->string[i] = ctr->options[i - lzh->Size]; + } + } +} + +/** + * @brief Set n-th module info to multiboot2 tag + * + * @param[in] hvld Loader handle + * @param[out] tag The buffer to be filled in. It's the caller's responsibility to allocate memory for the buffer + * + * @return None + */ +static void container_fill_module_tag(HV_LOADER hvld, struct multiboot2_tag_module *tag, UINTN index) +{ + struct container *ctr = (struct container *)hvld; + + LOADER_COMPRESSED_HEADER *cmd_lzh = NULL; + LOADER_COMPRESSED_HEADER *mod_lzh = NULL; + UINT8 * p = (UINT8 *)ctr->mod_hpa; + + UINTN i; + + for (i = LZH_MOD0_CMD; i < ctr->lzh_count - 1; i = i + 2) { + mod_lzh = ctr->lzh_ptr[i + 1]; + if (i == (index * 2 + LZH_MOD0_CMD)) { + cmd_lzh = ctr->lzh_ptr[i]; + tag->type = MULTIBOOT2_TAG_TYPE_MODULE; + tag->size = sizeof(struct multiboot2_tag_module) + cmd_lzh->Size; + tag->mod_start = (EFI_PHYSICAL_ADDRESS)p; + tag->mod_end = tag->mod_start + mod_lzh->Size; + memcpy(tag->cmdline, (char *)(uint64_t)cmd_lzh->Data, cmd_lzh->Size); + break; + } + p += ALIGN_UP(mod_lzh->Size, EFI_PAGE_SIZE); + } +} + +/** + * @brief Free up memory allocated by the container loader + * + * @param[in] hvld Loader handle + * + * @return None + */ +static void container_deinit(HV_LOADER hvld) +{ + struct container *ctr = (struct container *)hvld; + + if (ctr->lzh_ptr) { + free_pool(ctr->lzh_ptr); + free_pool(ctr); + } + + if (ctr->mod_hpa) { + free_pages(ctr->mod_hpa, EFI_SIZE_TO_PAGES(ctr->total_modsize)); + } +} + +/* hypervisor loader operation table */ +static struct hv_loader container_ops = { + .load_boot_image = container_load_boot_image, + .load_modules = container_load_modules, + .get_boot_cmdsize = container_get_boot_cmdsize, + .get_total_modsize = container_get_total_modsize, + .get_total_modcmdsize = container_get_total_modcmdsize, + .get_mod_count = container_get_mod_count, + .get_hv_hpa = container_get_hv_hpa, + .get_mod_hpa = container_get_mod_hpa, + .fill_bootcmd_tag = container_fill_bootcmd_tag, + .fill_module_tag = container_fill_module_tag, + .deinit = container_deinit, +}; + +/** + * @brief Initialize Container Library and returned the loader operation table + * + * @param[in] info Firmware-allocated handle that identifies the EFI application image (i.e. acrn.efi) + * @param[out] info Allocated loader operation table + * + * @return EFI_SUCCESS(0) on success, non-zero on error + */ +EFI_STATUS container_init(EFI_LOADED_IMAGE *info, HV_LOADER *hvld) +{ + EFI_STATUS err = EFI_SUCCESS; + + struct container *ctr = NULL; + + UINTN sec_addr = 0u; + UINTN sec_size = 0u; + char *section = ".hv"; + + UINTN i; + CONTAINER_HDR *hdr = NULL; + COMPONENT_ENTRY *comp = NULL; + + UINTN offset = 0u; + + err = allocate_pool(EfiLoaderData, sizeof(struct container), (void **)&ctr); + if (EFI_ERROR(err)) { + Print(L"Failed to allocate memory for Container Library %r\n", err); + goto out; + } + + (void)memset((void *)ctr, 0x0, sizeof(struct container)); + memcpy((char *)&ctr->ops, (const char *)&container_ops, sizeof(struct hv_loader)); + + /* store the options */ + ctr->options = info->LoadOptions; + ctr->options_size = info->LoadOptionsSize; + + /* read a container stitched at the .hv section */ + err = get_pe_section(info->ImageBase, section, strlen(section), &sec_addr, &sec_size); + if (EFI_ERROR(err)) { + Print(L"Unable to locate section of ACRNHV Container %r ", err); + goto out; + } + + hdr = (CONTAINER_HDR*)(info->ImageBase + sec_addr); + ctr->lzh_count = hdr->Count; + + err = allocate_pool(EfiLoaderData, sizeof(LOADER_COMPRESSED_HEADER *) * hdr->Count, (void **)&ctr->lzh_ptr); + if (EFI_ERROR(err)) { + Print(L"Failed to allocate memory for Container Library %r\n", err); + goto out; + } + + /* cache each file's header point for later use */ + comp = (COMPONENT_ENTRY *)(hdr + 1); + for (i = 0; i < hdr->Count; i++) { + offset = hdr->DataOffset + comp->Offset; + ctr->lzh_ptr[i] = (LOADER_COMPRESSED_HEADER *)((UINT8 *)(hdr) + offset); + + comp = (COMPONENT_ENTRY *)((UINT8 *)(comp + 1) + comp->HashSize); + } + + *hvld = (struct hv_loader *)ctr; +out: + if (EFI_ERROR(err)) { + if (ctr) { + container_deinit((HV_LOADER)ctr); + } + } + return err; +} diff --git a/misc/efi-stub/container.h b/misc/efi-stub/container.h new file mode 100644 index 000000000..26edc9580 --- /dev/null +++ b/misc/efi-stub/container.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __ACRNCONTAINER_H__ +#define __ACRNCONTAINER_H__ + +/** + * container_init - Initialize Container Library and returned the loader operation table. + * @info: Firmware-allocated handle that identifies the EFI application image (i.e. acrn.efi) + * @hvld: Loader operation table + */ +EFI_STATUS container_init(EFI_LOADED_IMAGE *info, HV_LOADER *hvld); + +#endif /* __ACRNCONTAINER_H__ */ diff --git a/misc/efi-stub/efilinux.h b/misc/efi-stub/efilinux.h new file mode 100644 index 000000000..7f0d5f211 --- /dev/null +++ b/misc/efi-stub/efilinux.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file contains some wrappers around the gnu-efi functions. As + * we're not going through uefi_call_wrapper() directly, this allows + * us to get some type-safety for function call arguments and for the + * compiler to check that the number of function call arguments is + * correct. + * + * It's also a good place to document the EFI interface. + */ + +#ifndef __EFILINUX_H__ +#define __EFILINUX_H__ + +#define EFILINUX_VERSION_MAJOR 1 +#define EFILINUX_VERSION_MINOR 0 + +#define MEM_ADDR_1MB (1U << 20U) +#define MEM_ADDR_4GB (0xFFFFFFFFU) + + +extern EFI_SYSTEM_TABLE *sys_table; +extern EFI_BOOT_SERVICES *boot; + +extern EFI_STATUS +emalloc_reserved_aligned(EFI_PHYSICAL_ADDRESS *addr, UINTN size, UINTN align, + EFI_PHYSICAL_ADDRESS minaddr, EFI_PHYSICAL_ADDRESS maxaddr); + +/** + * allocate_pages - Allocate memory pages from the system + * @atype: type of allocation to perform + * @mtype: type of memory to allocate + * @num_pages: number of contiguous 4KB pages to allocate + * @memory: used to return the address of allocated pages + * + * Allocate @num_pages physically contiguous pages from the system + * memory and return a pointer to the base of the allocation in + * @memory if the allocation succeeds. On success, the firmware memory + * map is updated accordingly. + * + * If @atype is AllocateAddress then, on input, @memory specifies the + * address at which to attempt to allocate the memory pages. + */ +static inline EFI_STATUS +allocate_pages(EFI_ALLOCATE_TYPE atype, EFI_MEMORY_TYPE mtype, + UINTN num_pages, EFI_PHYSICAL_ADDRESS *memory) +{ + return uefi_call_wrapper(boot->AllocatePages, 4, atype, + mtype, num_pages, memory); +} + +/** + * free_pages - Return memory allocated by allocate_pages() to the firmware + * @memory: physical base address of the page range to be freed + * @num_pages: number of contiguous 4KB pages to free + * + * On success, the firmware memory map is updated accordingly. + */ +static inline EFI_STATUS +free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN num_pages) +{ + return uefi_call_wrapper(boot->FreePages, 2, memory, num_pages); +} + +/** + * allocate_pool - Allocate pool memory + * @type: the type of pool to allocate + * @size: number of bytes to allocate from pool of @type + * @buffer: used to return the address of allocated memory + * + * Allocate memory from pool of @type. If the pool needs more memory + * pages are allocated from EfiConventionalMemory in order to grow the + * pool. + * + * All allocations are eight-byte aligned. + */ +static inline EFI_STATUS +allocate_pool(EFI_MEMORY_TYPE type, UINTN size, void **buffer) +{ + return uefi_call_wrapper(boot->AllocatePool, 3, type, size, buffer); +} + +/** + * free_pool - Return pool memory to the system + * @buffer: the buffer to free + * + * Return @buffer to the system. The returned memory is marked as + * EfiConventionalMemory. + */ +static inline EFI_STATUS free_pool(void *buffer) +{ + return uefi_call_wrapper(boot->FreePool, 1, buffer); +} + +/** + * get_memory_map - Return the current memory map + * @size: the size in bytes of @map + * @map: buffer to hold the current memory map + * @key: used to return the key for the current memory map + * @descr_size: used to return the size in bytes of EFI_MEMORY_DESCRIPTOR + * @descr_version: used to return the version of EFI_MEMORY_DESCRIPTOR + * + * Get a copy of the current memory map. The memory map is an array of + * EFI_MEMORY_DESCRIPTORs. An EFI_MEMORY_DESCRIPTOR describes a + * contiguous block of memory. + * + * On success, @key is updated to contain an identifer for the current + * memory map. The firmware's key is changed every time something in + * the memory map changes. @size is updated to indicate the size of + * the memory map pointed to by @map. + * + * @descr_size and @descr_version are used to ensure backwards + * compatibility with future changes made to the EFI_MEMORY_DESCRIPTOR + * structure. @descr_size MUST be used when the size of an + * EFI_MEMORY_DESCRIPTOR is used in a calculation, e.g when iterating + * over an array of EFI_MEMORY_DESCRIPTORs. + * + * On failure, and if the buffer pointed to by @map is too small to + * hold the memory map, EFI_BUFFER_TOO_SMALL is returned and @size is + * updated to reflect the size of a buffer required to hold the memory + * map. + */ +static inline EFI_STATUS +get_memory_map(UINTN *size, EFI_MEMORY_DESCRIPTOR *map, UINTN *key, + UINTN *descr_size, UINT32 *descr_version) +{ + return uefi_call_wrapper(boot->GetMemoryMap, 5, size, map, + key, descr_size, descr_version); +} + +/** + * exit_boot_serivces - Terminate all boot services + * @image: firmware-allocated handle that identifies the image + * @key: key to the latest memory map + * + * This function is called when efilinux wants to take complete + * control of the system. efilinux should not make calls to boot time + * services after this function is called. + */ +static inline EFI_STATUS +exit_boot_services(EFI_HANDLE image, UINTN key) +{ + return uefi_call_wrapper(boot->ExitBootServices, 2, image, key); +} + + +/** + * handle_protocol - Query @handle to see if it supports @protocol + * @handle: the handle being queried + * @protocol: the GUID of the protocol + * @interface: used to return the protocol interface + * + * Query @handle to see if @protocol is supported. If it is supported, + * @interface contains the protocol interface. + */ +static inline EFI_STATUS +handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface) +{ + return uefi_call_wrapper(boot->HandleProtocol, 3, + handle, protocol, interface); +} + + +/* + * emalloc_reserved_mem - it is called to allocate memory hypervisor itself + * and trampoline code, and mark the allocate memory as EfiReserved memory + * type so that SOS won't touch it during boot. + * @addr: a pointer to the allocated address on success + * @size: size in bytes of the requested allocation + * @max_addr: the allocated memory must be no more than this threshold + */ +static inline EFI_STATUS emalloc_reserved_mem(EFI_PHYSICAL_ADDRESS *addr, + UINTN size, EFI_PHYSICAL_ADDRESS max_addr) +{ + *addr = max_addr; + return allocate_pages(AllocateMaxAddress, EfiReservedMemoryType, + EFI_SIZE_TO_PAGES(size), addr); +} + + +/* + * emalloc_fixed_addr - it is called to allocate memory hypervisor itself + * when CONFIG_RELOC config is NOT enable.And mark the allocated memory as + * EfiReserved memory type so that SOS won't touch it during boot. + * @addr: a pointer to the allocated address on success + * @size: size in bytes of the requested allocation + */ +static inline EFI_STATUS emalloc_fixed_addr(EFI_PHYSICAL_ADDRESS *addr, + UINTN size, EFI_PHYSICAL_ADDRESS fixed_addr) +{ + *addr = fixed_addr; + return allocate_pages(AllocateAddress, EfiReservedMemoryType, + EFI_SIZE_TO_PAGES(size), addr); +} + +/** + * exit - Terminate a loaded EFI image + * @image: firmware-allocated handle that identifies the image + * @status: the image's exit code + * @size: size in bytes of @reason. Ignored if @status is EFI_SUCCESS + * @reason: a NUL-terminated status string, optionally followed by binary data + * + * This function terminates @image and returns control to the boot + * services. This function MUST NOT be called until all loaded child + * images have exited. All memory allocated by the image must be freed + * before calling this function, apart from the buffer @reason, which + * will be freed by the firmware. + */ +static inline EFI_STATUS +exit(EFI_HANDLE image, EFI_STATUS status, UINTN size, CHAR16 *reason) +{ + return uefi_call_wrapper(boot->Exit, 4, image, status, size, reason); +} + +#define PAGE_SIZE 4096 + +static const CHAR16 *memory_types[] = { + L"EfiReservedMemoryType", + L"EfiLoaderCode", + L"EfiLoaderData", + L"EfiBootServicesCode", + L"EfiBootServicesData", + L"EfiRuntimeServicesCode", + L"EfiRuntimeServicesData", + L"EfiConventionalMemory", + L"EfiUnusableMemory", + L"EfiACPIReclaimMemory", + L"EfiACPIMemoryNVS", + L"EfiMemoryMappedIO", + L"EfiMemoryMappedIOPortSpace", + L"EfiPalCode", +}; + +static inline const CHAR16 *memory_type_to_str(UINT32 type) +{ + if (type > sizeof(memory_types)/sizeof(CHAR16 *)) + return L"Unknown"; + + return memory_types[type]; +} + +extern EFI_STATUS memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, + UINTN *map_size, UINTN *map_key, + UINTN *desc_size, UINT32 *desc_version); + +#endif /* __EFILINUX_H__ */ diff --git a/misc/efi-stub/malloc.c b/misc/efi-stub/malloc.c new file mode 100644 index 000000000..1fc83bded --- /dev/null +++ b/misc/efi-stub/malloc.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "efilinux.h" +#include "stdlib.h" + +/** + * memory_map - Allocate and fill out an array of memory descriptors + * @map_buf: buffer containing the memory map + * @map_size: size of the buffer containing the memory map + * @map_key: key for the current memory map + * @desc_size: size of the desc + * @desc_version: memory descriptor version + * + * On success, @map_size contains the size of the memory map pointed + * to by @map_buf and @map_key, @desc_size and @desc_version are + * updated. + */ +EFI_STATUS +memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, + UINTN *map_key, UINTN *desc_size, UINT32 *desc_version) +{ + EFI_STATUS err; + + *map_size = sizeof(**map_buf) * 31; +get_map: + + /* + * Because we're about to allocate memory, we may + * potentially create a new memory descriptor, thereby + * increasing the size of the memory map. So increase + * the buffer size by the size of one memory + * descriptor, just in case. + */ + *map_size += sizeof(**map_buf); + + err = allocate_pool(EfiLoaderData, *map_size, + (void **)map_buf); + if (err != EFI_SUCCESS) { + Print(L"Failed to allocate pool for memory map"); + goto failed; + } + + err = get_memory_map(map_size, *map_buf, map_key, + desc_size, desc_version); + if (err != EFI_SUCCESS) { + if (err == EFI_BUFFER_TOO_SMALL) { + /* + * 'map_size' has been updated to reflect the + * required size of a map buffer. + */ + free_pool((void *)*map_buf); + goto get_map; + } + + Print(L"Failed to get memory map"); + goto failed; + } + +failed: + return err; +} + + +EFI_STATUS +emalloc_reserved_aligned(EFI_PHYSICAL_ADDRESS *addr, UINTN size, UINTN align, + EFI_PHYSICAL_ADDRESS minaddr, EFI_PHYSICAL_ADDRESS maxaddr) +{ + UINTN msize, mkey, desc_sz, desc_addr, pages; + UINT32 desc_version; + EFI_MEMORY_DESCRIPTOR *mbuf; + EFI_STATUS err; + + pages = EFI_SIZE_TO_PAGES(size); + + err = memory_map(&mbuf, &msize, &mkey, &desc_sz, &desc_version); + if (err != EFI_SUCCESS) { + goto fail; + } + + /* In most time, Memory map reported by BIOS is an ordering list from low to hight. + * Scan it from high to low, so that allocate memory as high as possible + */ + for (desc_addr = (UINTN)mbuf + msize - desc_sz; desc_addr >= (UINTN)mbuf; desc_addr -= desc_sz) { + EFI_MEMORY_DESCRIPTOR *desc; + EFI_PHYSICAL_ADDRESS start, end; + + desc = (EFI_MEMORY_DESCRIPTOR*)desc_addr; + if (desc->Type != EfiConventionalMemory) + continue; + + start = desc->PhysicalStart; + end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT); + + /* 1MB low memory is allocated only if required/requested */ + if ((end <= MEM_ADDR_1MB) && (maxaddr > MEM_ADDR_1MB)) + continue; + + /* starting allocation from 1M above unless requested */ + if ((start < MEM_ADDR_1MB) && (maxaddr > MEM_ADDR_1MB)) { + start = MEM_ADDR_1MB; + } + + /* zero page won't be allocated */ + if (start < 4096) { + start = 4096; + } + + if (start < minaddr) { + start = minaddr; + } + start = (start + align - 1) & ~(align - 1); + + /* Since this routine is called during booting, memory block is large + * enought, the reduction of memory size for memory alignment won't + * impact allocation. It is true in most cases. if it is not true, loop + * again + */ + if ((start + size <= end) && (start + size <= maxaddr)) { + err = allocate_pages(AllocateAddress, EfiReservedMemoryType, pages, &start); + if (err == EFI_SUCCESS) { + *addr = start; + break; + } + } + } + if (desc_addr < (UINTN)mbuf) { + err = EFI_OUT_OF_RESOURCES; + } + + free_pool(mbuf); + +fail: + return err; +} + +EFI_STATUS dump_e820(void) +{ + UINTN map_size, map_key, desc_size; + EFI_MEMORY_DESCRIPTOR *map_buf; + UINTN d, map_end; + UINTN i; + UINT32 desc_version; + EFI_STATUS err; + + err = memory_map(&map_buf, &map_size, &map_key, + &desc_size, &desc_version); + if (err != EFI_SUCCESS) + goto fail; + + d = (UINTN)map_buf; + map_end = (UINTN)map_buf + map_size; + + for (i = 0; d < map_end; d += desc_size, i++) { + EFI_MEMORY_DESCRIPTOR *desc; + EFI_PHYSICAL_ADDRESS start, end; + + desc = (EFI_MEMORY_DESCRIPTOR *)d; + if (desc->Type != EfiConventionalMemory) + continue; + + start = desc->PhysicalStart; + end = start + (desc->NumberOfPages << EFI_PAGE_SHIFT); + + Print(L"[%d]start:%lx, end:%lx, type:%d\n", i, start, end, desc->Type); + } + + free_pool(map_buf); +fail: + return err; +} + diff --git a/misc/efi-stub/multiboot.h b/misc/efi-stub/multiboot.h new file mode 100644 index 000000000..1af7a3ad8 --- /dev/null +++ b/misc/efi-stub/multiboot.h @@ -0,0 +1,386 @@ +/* [ORIGIN: src/sys/arch/i386/include/... */ +/* $NetBSD: multiboot.h,v 1.8 2009/02/22 18:05:42 ahoka Exp $ */ + +/*- + * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julio M. Merino Vidal. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * multiboot.h + */ + +#ifndef _MULTIBOOT_H +#define _MULTIBOOT_H + +#include +#include + +struct multiboot_info; +extern struct multiboot_info mbi; + +/* + * Multiboot header structure. + */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +#define MULTIBOOT_HEADER_MODS_ALIGNED 0x00000001 +#define MULTIBOOT_HEADER_WANT_MEMORY 0x00000002 +#define MULTIBOOT_HEADER_HAS_VBE 0x00000004 +#define MULTIBOOT_HEADER_HAS_ADDR 0x00010000 + +#if !defined(_LOCORE) +struct multiboot_header { + uint32_t mh_magic; + uint32_t mh_flags; + uint32_t mh_checksum; + + /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ + uint32_t mh_header_addr; + uint32_t mh_load_addr; + uint32_t mh_load_end_addr; + uint32_t mh_bss_end_addr; + uint32_t mh_entry_addr; + + /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. */ + uint32_t mh_mode_type; + uint32_t mh_width; + uint32_t mh_height; + uint32_t mh_depth; +}; +#endif /* !defined(_LOCORE) */ + +/* + * Symbols defined in locore.S. + */ +extern struct multiboot_header *Multiboot_Header; + +/* + * Multiboot information structure. + */ +#define MULTIBOOT_INFO_MAGIC 0x2BADB002U +#define MULTIBOOT_INFO_HAS_MEMORY 0x00000001U +#define MULTIBOOT_INFO_HAS_BOOT_DEVICE 0x00000002U +#define MULTIBOOT_INFO_HAS_CMDLINE 0x00000004U +#define MULTIBOOT_INFO_HAS_MODS 0x00000008U +#define MULTIBOOT_INFO_HAS_AOUT_SYMS 0x00000010U +#define MULTIBOOT_INFO_HAS_ELF_SYMS 0x00000020U +#define MULTIBOOT_INFO_HAS_MMAP 0x00000040U +#define MULTIBOOT_INFO_HAS_DRIVES 0x00000080U +#define MULTIBOOT_INFO_HAS_CONFIG_TABLE 0x00000100U +#define MULTIBOOT_INFO_HAS_LOADER_NAME 0x00000200U +#define MULTIBOOT_INFO_HAS_APM_TABLE 0x00000400U +#define MULTIBOOT_INFO_HAS_VBE 0x00000800U + +#if !defined(_LOCORE) +struct multiboot_info { + uint32_t mi_flags; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MEMORY. */ + uint32_t mi_mem_lower; + uint32_t mi_mem_upper; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_BOOT_DEVICE. */ + uint8_t mi_boot_device_part3; + uint8_t mi_boot_device_part2; + uint8_t mi_boot_device_part1; + uint8_t mi_boot_device_drive; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CMDLINE. */ + uint32_t mi_cmdline; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MODS. */ + uint32_t mi_mods_count; + uint32_t mi_mods_addr; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_{AOUT,ELF}_SYMS. */ + uint32_t mi_elfshdr_num; + uint32_t mi_elfshdr_size; + uint32_t mi_elfshdr_addr; + uint32_t mi_elfshdr_shndx; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MMAP. */ + uint32_t mi_mmap_length; + uint32_t mi_mmap_addr; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_DRIVES. */ + uint32_t mi_drives_length; + uint32_t mi_drives_addr; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CONFIG_TABLE. */ + uint32_t unused_mi_config_table; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_LOADER_NAME. */ + uint32_t mi_loader_name; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_APM. */ + uint32_t unused_mi_apm_table; + + /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_VBE. */ + uint32_t unused_mi_vbe_control_info; + uint32_t unused_mi_vbe_mode_info; + uint32_t unused_mi_vbe_interface_seg; + uint32_t unused_mi_vbe_interface_off; + uint32_t unused_mi_vbe_interface_len; +}__aligned(8); + + +/* + * Memory mapping. This describes an entry in the memory mappings table + * as pointed to by mi_mmap_addr. + * + * Be aware that mm_size specifies the size of all other fields *except* + * for mm_size. In order to jump between two different entries, you + * have to count mm_size + 4 bytes. + */ +struct __attribute__((packed)) multiboot_mmap { + uint32_t mm_size; + uint64_t mm_base_addr; + uint64_t mm_length; + uint32_t mm_type; +}; + +/* + * Modules. This describes an entry in the modules table as pointed + * to by mi_mods_addr. + */ + +struct multiboot_module { + uint32_t mmo_start; + uint32_t mmo_end; + uint32_t mmo_string; + uint32_t mmo_reserved; +} __packed; + +#endif /* !defined(_LOCORE) */ + +#ifdef CONFIG_MULTIBOOT2 +#define MB_INFO_MAGIC MULTIBOOT2_INFO_MAGIC +#else +#define MB_INFO_MAGIC MULTIBOOT_INFO_MAGIC +#endif + +#ifdef CONFIG_MULTIBOOT2 + +struct multiboot2_header +{ + uint32_t magic; + uint32_t architecture; + uint32_t header_length; + uint32_t checksum; +}; + +#define MULTIBOOT2_SEARCH 32768 + +#define MULTIBOOT2_HEADER_ALIGN 8 + +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6U + +/* This should be in %eax. */ +#define MULTIBOOT2_INFO_MAGIC 0x36d76289U + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT2_INFO_ALIGN 0x00000008U + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT2_TAG_ALIGN 8U +#define MULTIBOOT2_TAG_TYPE_END 0U +#define MULTIBOOT2_TAG_TYPE_CMDLINE 1U +#define MULTIBOOT2_TAG_TYPE_BOOT_LOADER_NAME 2U +#define MULTIBOOT2_TAG_TYPE_MODULE 3U +#define MULTIBOOT2_TAG_TYPE_BASIC_MEMINFO 4U +#define MULTIBOOT2_TAG_TYPE_BOOTDEV 5U +#define MULTIBOOT2_TAG_TYPE_MMAP 6U +#define MULTIBOOT2_TAG_TYPE_VBE 7U +#define MULTIBOOT2_TAG_TYPE_FRAMEBUFFER 8U +#define MULTIBOOT2_TAG_TYPE_ELF_SECTIONS 9U +#define MULTIBOOT2_TAG_TYPE_APM 10U +#define MULTIBOOT2_TAG_TYPE_EFI32 11U +#define MULTIBOOT2_TAG_TYPE_EFI64 12U +#define MULTIBOOT2_TAG_TYPE_SMBIOS 13U +#define MULTIBOOT2_TAG_TYPE_ACPI_OLD 14U +#define MULTIBOOT2_TAG_TYPE_ACPI_NEW 15U +#define MULTIBOOT2_TAG_TYPE_NETWORK 16U +#define MULTIBOOT2_TAG_TYPE_EFI_MMAP 17U +#define MULTIBOOT2_TAG_TYPE_EFI_BS 18U +#define MULTIBOOT2_TAG_TYPE_EFI32_IH 19U +#define MULTIBOOT2_TAG_TYPE_EFI64_IH 20U +#define MULTIBOOT2_TAG_TYPE_LOAD_BASE_ADDR 21U + +#define MULTIBOOT2_HEADER_TAG_END 0 +#define MULTIBOOT2_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT2_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT2_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT2_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT2_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT2_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI32 8 +#define MULTIBOOT2_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT2_HEADER_TAG_RELOCATABLE 10 +#define MULTIBOOT2_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT2_ARCHITECTURE_I386 0 +#define MULTIBOOT2_ARCHITECTURE_MIPS32 4 + +#ifndef ASSEMBLER + +struct multiboot2_header_tag +{ + uint16_t type; + uint16_t flags; + uint32_t size; +}; + +struct multiboot2_header_tag_information_request +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t requests[0]; +}; + +struct multiboot2_header_tag_address +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; +}; + +struct multiboot2_header_tag_entry_address +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t entry_addr; +}; + +struct multiboot2_header_tag_console_flags +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t console_flags; +}; + +struct multiboot2_header_tag_framebuffer +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t width; + uint32_t height; + uint32_t depth; +}; + +struct multiboot2_header_tag_module_align +{ + uint16_t type; + uint16_t flags; + uint32_t size; +}; + +struct multiboot2_header_tag_relocatable +{ + uint16_t type; + uint16_t flags; + uint32_t size; + uint32_t min_addr; + uint32_t max_addr; + uint32_t align; + uint32_t preference; +}; + +struct multiboot2_mmap_entry +{ + uint64_t addr; + uint64_t len; + uint32_t type; + uint32_t zero; +}; + +struct multiboot2_tag +{ + uint32_t type; + uint32_t size; +}; + +struct multiboot2_tag_string +{ + uint32_t type; + uint32_t size; + char string[0]; +}; + +struct multiboot2_tag_module +{ + uint32_t type; + uint32_t size; + uint32_t mod_start; + uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot2_tag_mmap +{ + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; + struct multiboot2_mmap_entry entries[0]; +}; + +struct multiboot2_tag_new_acpi +{ + uint32_t type; + uint32_t size; + uint8_t rsdp[0]; +}; + +struct multiboot2_tag_efi64 +{ + uint32_t type; + uint32_t size; + uint64_t pointer; +}; + +struct multiboot2_tag_efi_mmap { + uint32_t type; + uint32_t size; + uint32_t descr_size; + uint32_t descr_vers; + uint8_t efi_mmap[0]; +}; +#endif + +#endif /* CONFIG_MULTIBOOT2 */ + +#endif /* _MULTIBOOT_H */ diff --git a/misc/efi-stub/pe.c b/misc/efi-stub/pe.c new file mode 100644 index 000000000..0c734aa4d --- /dev/null +++ b/misc/efi-stub/pe.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file contains some wrappers around the gnu-efi functions. As + * we're not going through uefi_call_wrapper() directly, this allows + * us to get some type-safety for function call arguments and for the + * compiler to check that the number of function call arguments is + * correct. + * + * It's also a good place to document the EFI interface. + */ + +#include +#include +#include "stdlib.h" + +#define DOS_FILE_MAGIC_NUMBER 0x5A4D /* "MZ" */ +struct DosFileHeader { + uint16_t mMagic; + uint16_t LastSize; + uint16_t nBlocks; + uint16_t nReloc; + uint16_t HdrSize; + uint16_t MinAlloc; + uint16_t MaxAlloc; + uint16_t ss; + uint16_t sp; + uint16_t Checksum; + uint16_t ip; + uint16_t cs; + uint16_t RelocPos; + uint16_t nOverlay; + uint16_t reserved[4]; + uint16_t OEMId; + uint16_t OEMInfo; + uint16_t reserved2[10]; + uint32_t ExeHeader; +} __attribute__((packed)); + +#define IMAGE_FILE_MACHINE_I386 0x14c +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define PE_FILE_MAGIC_NUMBER 0x00004550 /* "PE\0\0" */ +struct PeHeader { + uint32_t mMagic; + uint16_t mMachine; + uint16_t mNumberOfSections; + uint32_t mTimeDateStamp; + uint32_t mPointerToSymbolTable; + uint32_t mNumberOfSymbols; + uint16_t mSizeOfOptionalHeader; + uint16_t mCharacteristics; +} __attribute__((packed)); + +struct OptionHeader { + uint16_t Format; + uint8_t MajorLinkVer; + uint8_t MinorLinkVer; + uint32_t CodeSize; + uint32_t InitializedDataSize; + uint32_t UninitializedDataSize; + uint32_t EntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfDate; +} __attribute__((packed)); + + +struct PeSectionHeader { + char mName[8]; + uint32_t mVirtualSize; + uint32_t mVirtualAddress; + uint32_t mSizeOfRawData; + uint32_t mPointerToRawData; + uint32_t mPointerToRealocations; + uint32_t mPointerToLinenumbers; + uint16_t mNumberOfRealocations; + uint16_t mNumberOfLinenumbers; + uint32_t mCharacteristics; +} __attribute__((packed)); + + +EFI_STATUS get_pe_section(CHAR8 *base, char *section_name, + UINTN section_name_len, UINTN *vaddr, UINTN *size) +{ + struct PeSectionHeader *ph; + struct DosFileHeader *dh; + struct PeHeader *pe; + UINTN i; + UINTN offset; + + dh = (struct DosFileHeader *)base; + + if (dh->mMagic != DOS_FILE_MAGIC_NUMBER) + return EFI_LOAD_ERROR; + + pe = (struct PeHeader *)&base[dh->ExeHeader]; + if (pe->mMagic != PE_FILE_MAGIC_NUMBER) + return EFI_LOAD_ERROR; + + if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64) + && (pe->mMachine != IMAGE_FILE_MACHINE_I386)) + return EFI_LOAD_ERROR; + + offset = dh->ExeHeader + sizeof(*pe) + pe->mSizeOfOptionalHeader; + + for (i = 0; i < pe->mNumberOfSections; i++) { + ph = (struct PeSectionHeader *)&base[offset]; + if (CompareMem(ph->mName, section_name, section_name_len) == 0) { + *vaddr = (UINTN)ph->mVirtualAddress; + *size = (UINTN)ph->mVirtualSize; + break; + } + + offset += sizeof(*ph); + } + + return EFI_SUCCESS; +} + + +EFI_IMAGE_ENTRY_POINT get_pe_entry(CHAR8 *base) +{ + struct DosFileHeader* dh; + struct PeHeader* pe; + struct OptionHeader* oh; + UINTN offset; + + dh = (struct DosFileHeader *)base; + + if (dh->mMagic != DOS_FILE_MAGIC_NUMBER) + return NULL; + + pe = (struct PeHeader *)&base[dh->ExeHeader]; + if (pe->mMagic != PE_FILE_MAGIC_NUMBER) + return NULL; + + if ((pe->mMachine != IMAGE_FILE_MACHINE_AMD64) + && (pe->mMachine != IMAGE_FILE_MACHINE_I386)) + return NULL; + + offset = dh->ExeHeader + sizeof(*pe); + oh = (struct OptionHeader*)&base[offset]; + + return (EFI_IMAGE_ENTRY_POINT)((UINT64)base + oh->EntryPoint); +} diff --git a/misc/efi-stub/stdlib.h b/misc/efi-stub/stdlib.h new file mode 100644 index 000000000..a8d8e2007 --- /dev/null +++ b/misc/efi-stub/stdlib.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011 - 2021, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file contains some wrappers around the gnu-efi functions. As + * we're not going through uefi_call_wrapper() directly, this allows + * us to get some type-safety for function call arguments and for the + * compiler to check that the number of function call arguments is + * correct. + * + * It's also a good place to document the EFI interface. + */ + + + +#ifndef __STDLIB_H__ +#define __STDLIB_H__ + + +static inline void memset(void *dstv, char ch, UINTN size) +{ + char *dst = dstv; + int32_t i; + + for (i = 0; i < size; i++) + dst[i] = ch; +} + +static inline void memcpy(char *dst, const char *src, UINTN size) +{ + int32_t i; + + for (i = 0; i < size; i++) + *dst++ = *src++; +} + +static inline int32_t strlen(const char *str) +{ + int32_t len; + + len = 0; + while (*str++) + len++; + + return len; +} + +static inline CHAR16 *strstr_16(CHAR16 *haystack, CHAR16 *needle, UINTN len) +{ + CHAR16 *p; + CHAR16 *word = NULL; + + if (!len) + return NULL; + + p = haystack; + while (*p) { + if (!StrnCmp(p, needle, len)) { + word = p; + break; + } + p++; + } + + return (CHAR16*)word; +} + +static inline char *ch16_2_ch8(CHAR16 *str16, UINTN len) +{ + UINTN i; + char *str8; + + str8 = AllocatePool((len + 1) * sizeof(char)); + + for (i = 0; i < len; i++) + str8[i] = str16[i]; + + str8[len] = 0; + + return str8; +} + +static inline CHAR16 *ch8_2_ch16(char *str8, UINTN len) +{ + UINTN i; + CHAR16 *str16; + + str16 = AllocatePool((len + 1) * sizeof(CHAR16)); + + for (i = 0; i < len; i++) + str16[i] = str8[i]; + + str16[len] = 0; + + return str16; +} + +#endif /* __STDLIB_H__ */