VOID EFIAPI PrintString ( IN CONST CHAR8 *FormatString, ... ) { #if (DEBUG_PRINT_LEVEL & 0x7FFFFFFF) UINTN Index; CHAR8 PrintBuffer[1000]; VA_LIST Marker; VA_START (Marker, FormatString); AsciiVSPrint (PrintBuffer, sizeof (PrintBuffer), FormatString, Marker); VA_END (Marker); for (Index = 0; PrintBuffer[Index] != 0; Index++) { if (PrintBuffer[Index] == '\n') { mCursor = (UINT8 *) (UINTN) (0xb8000 + (((((UINTN)mCursor - 0xb8000) + 160) / 160) * 160)); } else if (PrintBuffer[Index] != '\r') { // skip non-displayable character *mCursor = (UINT8) PrintBuffer[Index]; mCursor += 2; } } // // All information also output to serial port. // SerialPortWrite ((UINT8 *) PrintBuffer, Index); #endif }
VOID EFIAPI PrintString ( IN CONST CHAR8 *FormatString, ... ) { UINTN Index; CHAR8 PrintBuffer[256]; VA_LIST Marker; VA_START (Marker, FormatString); AsciiVSPrint (PrintBuffer, sizeof (PrintBuffer), FormatString, Marker); VA_END (Marker); for (Index = 0; PrintBuffer[Index] != 0; Index++) { if (PrintBuffer[Index] == '\n') { mCursor = (UINT8 *) (UINTN) (0xb8000 + (((((UINTN)mCursor - 0xb8000) + 160) / 160) * 160)); } else { *mCursor = (UINT8) PrintBuffer[Index]; mCursor += 2; } } // // All information also output to serial port. // SerialPortWrite ((UINT8 *) PrintBuffer, Index); }
/** Prints an assert message containing a filename, line number, and description. This may be followed by a breakpoint or a dead loop. Print a message of the form "ASSERT <FileName>(<LineNumber>): <Description>\n" to the debug output device. If DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED bit of PcdDebugProperyMask is set then CpuBreakpoint() is called. Otherwise, if DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED bit of PcdDebugProperyMask is set then CpuDeadLoop() is called. If neither of these bits are set, then this function returns immediately after the message is printed to the debug output device. DebugAssert() must actively prevent recursion. If DebugAssert() is called while processing another DebugAssert(), then DebugAssert() must return immediately. If FileName is NULL, then a <FileName> string of "(NULL) Filename" is printed. If Description is NULL, then a <Description> string of "(NULL) Description" is printed. @param FileName The pointer to the name of the source file that generated the assert condition. @param LineNumber The line number in the source file that generated the assert condition @param Description The pointer to the description of the assert condition. **/ VOID EFIAPI DebugAssert ( IN CONST CHAR8 *FileName, IN UINTN LineNumber, IN CONST CHAR8 *Description ) { CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; // // Generate the ASSERT() message in Ascii format // AsciiSPrint (Buffer, sizeof (Buffer), "ASSERT %a(%d): %a\n", FileName, LineNumber, Description); // // Send the print string to the Console Output device // AcquireSpinLock (&mInternalDebugLock); SerialPortWrite ((UINT8 *) Buffer, AsciiStrLen(Buffer)); ReleaseSpinLock (&mInternalDebugLock); // // Generate a Breakpoint, DeadLoop, or NOP based on PCD settings // if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_BREAKPOINT_ENABLED) != 0) { CpuBreakpoint (); } else if ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_ASSERT_DEADLOOP_ENABLED) != 0) { CpuDeadLoop (); } }
VOID PeiCommonExceptionEntry ( IN UINT32 Entry, IN UINTN LR ) { CHAR8 Buffer[100]; UINTN CharCount; switch (Entry) { case EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS: CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Synchronous Exception at 0x%X\n\r", LR); break; case EXCEPT_AARCH64_IRQ: CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"IRQ Exception at 0x%X\n\r", LR); break; case EXCEPT_AARCH64_FIQ: CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"FIQ Exception at 0x%X\n\r", LR); break; case EXCEPT_AARCH64_SERROR: CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"SError/Abort Exception at 0x%X\n\r", LR); break; default: CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Unknown Exception at 0x%X\n\r", LR); break; } SerialPortWrite ((UINT8 *) Buffer, CharCount); while(1); }
/** Call before jumping to Normal World This function allows the firmware platform to do extra actions before jumping to the Normal World **/ VOID ArmPlatformSecExtraAction ( IN UINTN MpId, OUT UINTN* JumpAddress ) { CHAR8 Buffer[100]; UINTN CharCount; if (FeaturePcdGet (PcdStandalone) == FALSE) { // // Warning: This code assumes the DRAM has already been initialized by ArmPlatformSecLib // if (IS_PRIMARY_CORE(MpId)) { UINTN* StartAddress = (UINTN*)PcdGet32(PcdFvBaseAddress); // Patch the DRAM to make an infinite loop at the start address *StartAddress = 0xEAFFFFFE; // opcode for while(1) CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Waiting for firmware at 0x%08X ...\n\r",StartAddress); SerialPortWrite ((UINT8 *) Buffer, CharCount); *JumpAddress = PcdGet32(PcdFvBaseAddress); } else { // When the primary core is stopped by the hardware debugger to copy the firmware // into DRAM. The secondary cores are still running. As soon as the first bytes of // the firmware are written into DRAM, the secondary cores will start to execute the // code even if the firmware is not entirely written into the memory. // That's why the secondary cores need to be parked in WFI and wake up once the // firmware is ready. *JumpAddress = (UINTN)NonSecureWaitForFirmware; } } else if (FeaturePcdGet (PcdSystemMemoryInitializeInSec)) { // // Warning: This code assumes the DRAM has already been initialized by ArmPlatformSecLib // if (IS_PRIMARY_CORE(MpId)) { // Signal the secondary cores they can jump to PEI phase ArmGicSendSgiTo (PcdGet32(PcdGicDistributorBase), ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E, PcdGet32 (PcdGicSgiIntId)); // To enter into Non Secure state, we need to make a return from exception *JumpAddress = PcdGet32(PcdFvBaseAddress); } else { // We wait for the primary core to finish to initialize the System Memory. Otherwise the secondary // cores would make crash the system by setting their stacks in DRAM before the primary core has not // finished to initialize the system memory. *JumpAddress = (UINTN)NonSecureWaitForFirmware; } } else { *JumpAddress = PcdGet32(PcdFvBaseAddress); } }
/** Write data from buffer to debug device. Writes NumberOfBytes data bytes from Buffer to the debug device. The number of bytes actually written to the debug device is returned. If the return value is less than NumberOfBytes, then the write operation failed. If NumberOfBytes is zero, then return 0. @param Handle Debug port handle. @param Buffer Pointer to the data buffer to be written. @param NumberOfBytes Number of bytes to written to the debug device. @retval 0 NumberOfBytes is 0. @retval >0 The number of bytes written to the debug device. If this value is less than NumberOfBytes, then the read operation failed. **/ UINTN EFIAPI DebugPortWriteBuffer ( IN DEBUG_PORT_HANDLE Handle, IN UINT8 *Buffer, IN UINTN NumberOfBytes ) { return SerialPortWrite (Buffer, NumberOfBytes); }
/** Notification function of the event defined as belonging to the EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in the entry point of the driver. This function is called when an event belonging to the EFI_END_OF_DXE_EVENT_GROUP_GUID event group is signalled. Such an event is signalled once at the end of the dispatching of all drivers (end of the so called DXE phase). @param[in] Event Event declared in the entry point of the driver whose notification function is being invoked. @param[in] Context NULL **/ STATIC VOID OnEndOfDxe ( IN EFI_EVENT Event, IN VOID *Context ) { UINT32 BootMode; VOID *RecoveryStr; VOID *SwitchStr; BootMode = MmioRead32 (SCTRL_BAK_DATA0) & BOOT_MODE_MASK; if (BootMode == BOOT_MODE_RECOVERY) { RecoveryStr = "WARNING: CAN NOT BOOT KERNEL IN RECOVERY MODE!\r\n"; SwitchStr = "Switch to normal boot mode, then reboot to boot kernel.\r\n"; SerialPortWrite (RecoveryStr, AsciiStrLen (RecoveryStr)); SerialPortWrite (SwitchStr, AsciiStrLen (SwitchStr)); } }
VOID EFIAPI SerialPortWriteHex1 ( UINTN Data ) { UINT8 Buffer[3]; Buffer[0] = NibbleToAscii(Data >> 4); Buffer[1] = NibbleToAscii(Data); Buffer[2] = 0; SerialPortWrite (&Buffer[0], sizeof(Buffer)); }
/** Writes data to a serial device. @param This Protocol instance pointer. @param BufferSize On input, the size of the Buffer. On output, the amount of data actually written. @param Buffer The buffer of data to write @retval EFI_SUCCESS The data was written. @retval EFI_DEVICE_ERROR The device reported an error. @retval EFI_TIMEOUT The data write was stopped due to a timeout. **/ EFI_STATUS EFIAPI SerialWrite ( IN EFI_SERIAL_IO_PROTOCOL *This, IN OUT UINTN *BufferSize, IN VOID *Buffer ) { UINTN Count; Count = SerialPortWrite (Buffer, *BufferSize); if (Count != *BufferSize) { *BufferSize = Count; return EFI_TIMEOUT; } return EFI_SUCCESS; }
EFI_STATUS EFIAPI OutputString ( IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, IN CHAR16 *String ) { UINTN Size = StrLen(String) + 1; CHAR8 *OutputString = AllocatePool(Size); //If there is any non-ascii characters in String buffer then replace it with '?' //Eventually, UnicodeStrToAsciiStr API should be fixed. SafeUnicodeStrToAsciiStr(String, OutputString); SerialPortWrite ((UINT8 *)OutputString, Size - 1); FreePool(OutputString); return EFI_SUCCESS; }
/** Prints a message to the serial port. @param Format Format string for the message to print. @param ... Variable argument list whose contents are accessed based on the format string specified by Format. **/ VOID EFIAPI InternalPrintMessage ( IN CONST CHAR8 *Format, ... ) { CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; VA_LIST Marker; // // Convert the message to an ASCII String // VA_START (Marker, Format); AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); VA_END (Marker); // // Send the print string to a Serial Port // SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer)); }
/** Prints a debug message to the debug output device if the specified error level is enabled. If any bit in ErrorLevel is also set in PcdDebugPrintErrorLevel, then print the message specified by Format and the associated variable argument list to the debug output device. If Format is NULL, then ASSERT(). @param ErrorLevel The error level of the debug message. @param Format Format string for the debug message to print. @param ... Variable argument list whose contents are accessed based on the format string specified by Format. **/ VOID EFIAPI DebugPrint ( IN UINTN ErrorLevel, IN CONST CHAR8 *Format, ... ) { CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH]; VA_LIST Marker; // // If Format is NULL, then ASSERT(). // ASSERT (Format != NULL); // // Check driver debug mask value and global mask // if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0) { return; } // // Convert the DEBUG() message to an ASCII String // VA_START (Marker, Format); AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker); VA_END (Marker); // // Send the print string to a Serial Port // AcquireSpinLock (&mInternalDebugLock); SerialPortWrite ((UINT8 *) Buffer, AsciiStrLen(Buffer)); ReleaseSpinLock (&mInternalDebugLock); }
VOID CEntryPoint ( IN UINTN MpId, IN UINTN SecBootMode ) { CHAR8 Buffer[100]; UINTN CharCount; UINTN JumpAddress; // Invalidate the data cache. Doesn't have to do the Data cache clean. ArmInvalidateDataCache (); // Invalidate Instruction Cache ArmInvalidateInstructionCache (); // Invalidate I & D TLBs ArmInvalidateInstructionAndDataTlb (); // CPU specific settings ArmCpuSetup (MpId); // Enable Floating Point Coprocessor if supported by the platform if (FixedPcdGet32 (PcdVFPEnabled)) { ArmEnableVFP (); } // Initialize peripherals that must be done at the early stage // Example: Some L2 controller, interconnect, clock, DMC, etc ArmPlatformSecInitialize (MpId); // Primary CPU clears out the SCU tag RAMs, secondaries wait if (ArmPlatformIsPrimaryCore (MpId) && (SecBootMode == ARM_SEC_COLD_BOOT)) { if (ArmIsMpCore()) { // Signal for the initial memory is configured (event: BOOT_MEM_INIT) ArmCallSEV (); } // SEC phase needs to run library constructors by hand. This assumes we are linked against the SerialLib // In non SEC modules the init call is in autogenerated code. SerialPortInitialize (); // Start talking if (FixedPcdGetBool (PcdTrustzoneSupport)) { CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Secure firmware (version %s built at %a on %a)\n\r", (CHAR16*)PcdGetPtr(PcdFirmwareVersionString), __TIME__, __DATE__); } else { CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"Boot firmware (version %s built at %a on %a)\n\r", (CHAR16*)PcdGetPtr(PcdFirmwareVersionString), __TIME__, __DATE__); } SerialPortWrite ((UINT8 *) Buffer, CharCount); // Initialize the Debug Agent for Source Level Debugging InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL); SaveAndSetDebugTimerInterrupt (TRUE); // Enable the GIC distributor and CPU Interface // - no other Interrupts are enabled, doesn't have to worry about the priority. // - all the cores are in secure state, use secure SGI's ArmGicEnableDistributor (PcdGet32(PcdGicDistributorBase)); ArmGicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase)); } else { // Enable the GIC CPU Interface ArmGicEnableInterruptInterface (PcdGet32(PcdGicInterruptInterfaceBase)); } // Enable Full Access to CoProcessors ArmWriteCpacr (CPACR_CP_FULL_ACCESS); // Test if Trustzone is supported on this platform if (FixedPcdGetBool (PcdTrustzoneSupport)) { if (ArmIsMpCore ()) { // Setup SMP in Non Secure world ArmCpuSetupSmpNonSecure (GET_CORE_ID(MpId)); } // Either we use the Secure Stacks for Secure Monitor (in this case (Base == 0) && (Size == 0)) // Or we use separate Secure Monitor stacks (but (Base != 0) && (Size != 0)) ASSERT (((PcdGet32(PcdCPUCoresSecMonStackBase) == 0) && (PcdGet32(PcdCPUCoreSecMonStackSize) == 0)) || ((PcdGet32(PcdCPUCoresSecMonStackBase) != 0) && (PcdGet32(PcdCPUCoreSecMonStackSize) != 0))); // Enter Monitor Mode enter_monitor_mode ( (UINTN)TrustedWorldInitialization, MpId, SecBootMode, (VOID*) (PcdGet32 (PcdCPUCoresSecMonStackBase) + (PcdGet32 (PcdCPUCoreSecMonStackSize) * (ArmPlatformGetCorePosition (MpId) + 1))) ); } else { if (ArmPlatformIsPrimaryCore (MpId)) { SerialPrint ("Trust Zone Configuration is disabled\n\r"); } // With Trustzone support the transition from Sec to Normal world is done by return_from_exception(). // If we want to keep this function call we need to ensure the SVC's SPSR point to the same Program // Status Register as the the current one (CPSR). copy_cpsr_into_spsr (); // Call the Platform specific function to execute additional actions if required JumpAddress = PcdGet32 (PcdFvBaseAddress); ArmPlatformSecExtraAction (MpId, &JumpAddress); NonTrustedWorldTransition (MpId, JumpAddress); } ASSERT (0); // We must never return from the above function }
VOID PrePiMain ( IN UINTN UefiMemoryBase, IN UINTN StacksBase, IN UINT64 StartTimeStamp ) { EFI_HOB_HANDOFF_INFO_TABLE* HobList; EFI_STATUS Status; CHAR8 Buffer[100]; UINTN CharCount; UINTN StacksSize; // Initialize the architecture specific bits ArchInitialize (); // Declare the PI/UEFI memory region HobList = HobConstructor ( (VOID*)UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize), (VOID*)UefiMemoryBase, (VOID*)StacksBase // The top of the UEFI Memory is reserved for the stacks ); PrePeiSetHobList (HobList); // // Ensure that the loaded image is invalidated in the caches, so that any // modifications we made with the caches and MMU off (such as the applied // relocations) don't become invisible once we turn them on. // InvalidateDataCacheRange((VOID *)(UINTN)PcdGet64 (PcdFdBaseAddress), PcdGet32 (PcdFdSize)); // Initialize MMU and Memory HOBs (Resource Descriptor HOBs) Status = MemoryPeim (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize)); ASSERT_EFI_ERROR (Status); // Initialize the Serial Port SerialPortInitialize (); CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"UEFI firmware (version %s built at %a on %a)\n\r", (CHAR16*)PcdGetPtr(PcdFirmwareVersionString), __TIME__, __DATE__); SerialPortWrite ((UINT8 *) Buffer, CharCount); // Create the Stacks HOB (reserve the memory for all stacks) StacksSize = PcdGet32 (PcdCPUCorePrimaryStackSize); BuildStackHob (StacksBase, StacksSize); //TODO: Call CpuPei as a library BuildCpuHob (PcdGet8 (PcdPrePiCpuMemorySize), PcdGet8 (PcdPrePiCpuIoSize)); // Set the Boot Mode SetBootMode (ArmPlatformGetBootMode ()); // Initialize Platform HOBs (CpuHob and FvHob) Status = PlatformPeim (); ASSERT_EFI_ERROR (Status); // Now, the HOB List has been initialized, we can register performance information PERF_START (NULL, "PEI", NULL, StartTimeStamp); // SEC phase needs to run library constructors by hand. ExtractGuidedSectionLibConstructor (); LzmaDecompressLibConstructor (); // Build HOBs to pass up our version of stuff the DXE Core needs to save space BuildPeCoffLoaderHob (); BuildExtractSectionHob ( &gLzmaCustomDecompressGuid, LzmaGuidedSectionGetInfo, LzmaGuidedSectionExtraction ); // Assume the FV that contains the SEC (our code) also contains a compressed FV. Status = DecompressFirstFv (); ASSERT_EFI_ERROR (Status); // Load the DXE Core and transfer control to it Status = LoadDxeCoreFromFv (NULL, 0); ASSERT_EFI_ERROR (Status); }
VOID PrePiMain ( IN UINTN UefiMemoryBase, IN UINTN StacksBase, IN UINT64 StartTimeStamp ) { EFI_HOB_HANDOFF_INFO_TABLE* HobList; ARM_MP_CORE_INFO_PPI* ArmMpCoreInfoPpi; UINTN ArmCoreCount; ARM_CORE_INFO* ArmCoreInfoTable; EFI_STATUS Status; CHAR8 Buffer[100]; UINTN CharCount; UINTN StacksSize; // If ensure the FD is either part of the System Memory or totally outside of the System Memory (XIP) ASSERT (IS_XIP() || ((FixedPcdGet64 (PcdFdBaseAddress) >= FixedPcdGet64 (PcdSystemMemoryBase)) && ((UINT64)(FixedPcdGet64 (PcdFdBaseAddress) + FixedPcdGet32 (PcdFdSize)) <= (UINT64)mSystemMemoryEnd))); // Initialize the architecture specific bits ArchInitialize (); // Initialize the Serial Port SerialPortInitialize (); CharCount = AsciiSPrint (Buffer,sizeof (Buffer),"UEFI firmware (version %s built at %a on %a)\n\r", (CHAR16*)PcdGetPtr(PcdFirmwareVersionString), __TIME__, __DATE__); SerialPortWrite ((UINT8 *) Buffer, CharCount); // Initialize the Debug Agent for Source Level Debugging InitializeDebugAgent (DEBUG_AGENT_INIT_POSTMEM_SEC, NULL, NULL); SaveAndSetDebugTimerInterrupt (TRUE); // Declare the PI/UEFI memory region HobList = HobConstructor ( (VOID*)UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize), (VOID*)UefiMemoryBase, (VOID*)StacksBase // The top of the UEFI Memory is reserved for the stacks ); PrePeiSetHobList (HobList); // Initialize MMU and Memory HOBs (Resource Descriptor HOBs) Status = MemoryPeim (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize)); ASSERT_EFI_ERROR (Status); // Create the Stacks HOB (reserve the memory for all stacks) if (ArmIsMpCore ()) { StacksSize = PcdGet32 (PcdCPUCorePrimaryStackSize) + ((FixedPcdGet32 (PcdCoreCount) - 1) * FixedPcdGet32 (PcdCPUCoreSecondaryStackSize)); } else { StacksSize = PcdGet32 (PcdCPUCorePrimaryStackSize); } BuildStackHob (StacksBase, StacksSize); //TODO: Call CpuPei as a library BuildCpuHob (PcdGet8 (PcdPrePiCpuMemorySize), PcdGet8 (PcdPrePiCpuIoSize)); if (ArmIsMpCore ()) { // Only MP Core platform need to produce gArmMpCoreInfoPpiGuid Status = GetPlatformPpi (&gArmMpCoreInfoPpiGuid, (VOID**)&ArmMpCoreInfoPpi); // On MP Core Platform we must implement the ARM MP Core Info PPI (gArmMpCoreInfoPpiGuid) ASSERT_EFI_ERROR (Status); // Build the MP Core Info Table ArmCoreCount = 0; Status = ArmMpCoreInfoPpi->GetMpCoreInfo (&ArmCoreCount, &ArmCoreInfoTable); if (!EFI_ERROR(Status) && (ArmCoreCount > 0)) { // Build MPCore Info HOB BuildGuidDataHob (&gArmMpCoreInfoGuid, ArmCoreInfoTable, sizeof (ARM_CORE_INFO) * ArmCoreCount); } } // Set the Boot Mode SetBootMode (ArmPlatformGetBootMode ()); // Initialize Platform HOBs (CpuHob and FvHob) Status = PlatformPeim (); ASSERT_EFI_ERROR (Status); // Now, the HOB List has been initialized, we can register performance information PERF_START (NULL, "PEI", NULL, StartTimeStamp); // SEC phase needs to run library constructors by hand. ExtractGuidedSectionLibConstructor (); LzmaDecompressLibConstructor (); // Build HOBs to pass up our version of stuff the DXE Core needs to save space BuildPeCoffLoaderHob (); BuildExtractSectionHob ( &gLzmaCustomDecompressGuid, LzmaGuidedSectionGetInfo, LzmaGuidedSectionExtraction ); // Assume the FV that contains the SEC (our code) also contains a compressed FV. Status = DecompressFirstFv (); ASSERT_EFI_ERROR (Status); // Load the DXE Core and transfer control to it Status = LoadDxeCoreFromFv (NULL, 0); ASSERT_EFI_ERROR (Status); }
EFI_STATUS EFIAPI OutputString ( IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, IN CHAR16 *String ) { UINTN Size; CHAR8* OutputString; EFI_STATUS Status; EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; UINTN MaxColumn; UINTN MaxRow; Size = StrLen(String) + 1; OutputString = AllocatePool(Size); //If there is any non-ascii characters in String buffer then replace it with '?' //Eventually, UnicodeStrToAsciiStr API should be fixed. SafeUnicodeStrToAsciiStr(String, OutputString); SerialPortWrite ((UINT8 *)OutputString, Size - 1); // // Parse each character of the string to output // to update the cursor position information // Mode = This->Mode; Status = This->QueryMode ( This, Mode->Mode, &MaxColumn, &MaxRow ); if (EFI_ERROR (Status)) { return Status; } for (; *String != CHAR_NULL; String++) { switch (*String) { case CHAR_BACKSPACE: if (Mode->CursorColumn > 0) { Mode->CursorColumn--; } break; case CHAR_LINEFEED: if (Mode->CursorRow < (INT32) (MaxRow - 1)) { Mode->CursorRow++; } break; case CHAR_CARRIAGE_RETURN: Mode->CursorColumn = 0; break; default: if (Mode->CursorColumn >= (INT32) (MaxColumn - 1)) { // Move the cursor as if we print CHAR_CARRIAGE_RETURN & CHAR_LINE_FEED // CHAR_LINEFEED if (Mode->CursorRow < (INT32) (MaxRow - 1)) { Mode->CursorRow++; } // CHAR_CARIAGE_RETURN Mode->CursorColumn = 0; } else { Mode->CursorColumn++; } break; } } FreePool(OutputString); return EFI_SUCCESS; }