/**
Macro that calls DebugPrint().
If MDEPKG_NDEBUG is not defined and the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED
bit of PcdDebugProperyMask is set, then this macro passes Expression to
DebugPrint().
@param Expression Expression containing an error level, a format string,
and a variable argument list based on the format string.
**/
#if !defined(MDEPKG_NDEBUG)
#define DEBUG(Expression) \
do { \
if (DebugPrintEnabled ()) { \
_DEBUG (Expression); \
} \
} while (FALSE)
#else
#define DEBUG(Expression)
#endif
/**
Returns TRUE if DEBUG() macros are enabled.
This function returns TRUE if the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of
PcdDebugProperyMask is set. Otherwise FALSE is returned.
@retval TRUE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is set.
@retval FALSE The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is clear.
**/
BOOLEAN
EFIAPI
DebugPrintEnabled (
VOID
)
{
return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) & DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
}
/**
Internal worker macro that calls DebugPrint().
This macro calls DebugPrint() passing in the debug error level, a format
string, and a variable argument list.
__VA_ARGS__ is not supported by EBC compiler, Microsoft Visual Studio .NET 2003
and Microsoft Windows Server 2003 Driver Development Kit (Microsoft WINDDK) version 3790.1830.
@param Expression Expression containing an error level, a format string,
and a variable argument list based on the format string.
**/
#if !defined(MDE_CPU_EBC) && (!defined (_MSC_VER) || _MSC_VER > 1400)
#define _DEBUG_PRINT(PrintLevel, ...) \
do { \
if (DebugPrintLevelEnabled (PrintLevel)) { \
DebugPrint (PrintLevel, ##__VA_ARGS__); \
} \
} while (FALSE)
#define _DEBUG(Expression) _DEBUG_PRINT Expression
#else
#define _DEBUG(Expression) DebugPrint Expression
#endif
/**
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 DebugPrintErrorLevelLib function
GetDebugPrintErrorLevel (), 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,
...
)
{
VA_LIST Marker;
VA_START (Marker, Format);
DebugVPrint (ErrorLevel, Format, Marker);
VA_END (Marker);
}
1.2.2 同一个文件中,定义了函数DebugVPrint()
/**
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 DebugPrintErrorLevelLib function
GetDebugPrintErrorLevel (), 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 VaListMarker VA_LIST marker for the variable argument list.
**/
VOID
EFIAPI
DebugVPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
IN VA_LIST VaListMarker
)
{
DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL);
}
1.2.3 同一个文件中 DebugPrintMarker() 代码如下:
/**
Prints a debug message to the debug output device if the specified
error level is enabled base on Null-terminated format string and a
VA_LIST argument list or a BASE_LIST argument list.
If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
GetDebugPrintErrorLevel (), 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 VaListMarker VA_LIST marker for the variable argument list.
@param BaseListMarker BASE_LIST marker for the variable argument list.
**/
VOID
DebugPrintMarker (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
IN VA_LIST VaListMarker,
IN BASE_LIST BaseListMarker
)
{
CHAR8 Buffer[MAX_DEBUG_MESSAGE_LENGTH];
UINTN Length;
//
// If Format is NULL, then ASSERT().
//
ASSERT (Format != NULL);
//
// Check if the global mask disables this message or the device is inactive
//
if ((ErrorLevel & GetDebugPrintErrorLevel ()) == 0 ||
!PlatformDebugLibIoPortFound ()) {
return;
}
//
// Convert the DEBUG() message to an ASCII String
//
if (BaseListMarker == NULL) {
Length = AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);
} else {
Length = AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);
}
//
// Send the print string to the debug I/O port
//
IoWriteFifo8 (PcdGet16 (PcdDebugIoPort), Length, Buffer);
}
其中PcdDebugIoPort定义在 \OvmfPkg\OvmfPkg.dec文件中:
## This flag is used to control the destination port for PlatformDebugLibIoPort
gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort|0x402|UINT16|4
BITS 64
;
; Some values were calculated in 32-bit mode. Make sure the upper
; 32-bits of 64-bit registers are zero for these values.
;
mov rax, 0x00000000ffffffff
and rsi, rax
and rbp, rax
and rsp, rax
;
; RSI - SEC Core entry point
; RBP - Start of BFV
;
;
; Restore initial EAX value into the RAX register
;
mov rax, rsp
;
; Jump to the 64-bit SEC entry point
;
jmp rsi
这里加入代码,从0x402 Port输出 rsi的值:
;
; Restore initial EAX value into the RAX register
;
mov rax, rsp
mov rax,rsi
mov dx,0x402
; 7-0 Bits
out dx,al
; 15-8 Bits
shr rax,8
out dx,al
; 23-16 Bits
shr rax,8
out dx,al
; 31-24 Bits
shr rax,8
out dx,al
; 39-32 Bits
shr rax,8
out dx,al
; 47-40 Bits
shr rax,8
out dx,al
; 55-48 Bits
shr rax,8
out dx,al
; 63-56 Bits
shr rax,8
out dx,al
接下来在SecEntry.nasm添加代码,输出当前 rip 的值:
;
; SecCore Entry Point
;
; Processor is in flat protected mode
;
; @param[in] RAX Initial value of the EAX register (BIST: Built-in Self Test)
; @param[in] DI 'BP': boot-strap processor, or 'AP': application processor
; @param[in] RBP Pointer to the start of the Boot Firmware Volume
; @param[in] DS Selector allowing flat access to all addresses
; @param[in] ES Selector allowing flat access to all addresses
; @param[in] FS Selector allowing flat access to all addresses
; @param[in] GS Selector allowing flat access to all addresses
; @param[in] SS Selector allowing flat access to all addresses
;
; @return None This routine does not return
;
global ASM_PFX(_ModuleEntryPoint)
ASM_PFX(_ModuleEntryPoint):
lea rax,[$]
mov dx,0x402
; 7-0 Bits
out dx,al
; 15-8 Bits
shr rax,8
out dx,al
; 23-16 Bits
shr rax,8
out dx,al
; 31-24 Bits
shr rax,8
out dx,al
; 39-32 Bits
shr rax,8
out dx,al
; 47-40 Bits
shr rax,8
out dx,al
; 55-48 Bits
shr rax,8
out dx,al
; 63-56 Bits
shr rax,8
out dx,al
;
; Fill the temporary RAM with the initial stack value.
; The loop below will seed the heap as well, but that's harmless.
;
mov rax, (FixedPcdGet32 (PcdInitValueInTempStack) << 32) | FixedPcdGet32 (PcdInitValueInTempStack)
; qword to store
mov rdi, FixedPcdGet32 (PcdOvmfSecPeiTempRamBase) ; base address,
; relative to
; ES
mov rcx, FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) / 8 ; qword count
cld ; store from base
; up
rep stosq
;
; The VTF signature
;
; VTF-0 means that the VTF (Volume Top File) code does not require
; any fixups.
;
vtfSignature:
DB 'V', 'T', 'F', 0
ALIGN 16
resetVector:
;
; Reset Vector
;
; This is where the processor will begin execution
;
nop
nop
jmp EarlyBspInitReal16
ALIGN 16
fourGigabytes:
;
; Modified: EBX, ECX, EDX, EBP
;
; @param[in,out] RAX/EAX Initial value of the EAX register
; (BIST: Built-in Self Test)
; @param[in,out] DI 'BP': boot-strap processor, or
; 'AP': application processor
; @param[out] RBP/EBP Address of Boot Firmware Volume (BFV)
; @param[out] DS Selector allowing flat access to all addresses
; @param[out] ES Selector allowing flat access to all addresses
; @param[out] FS Selector allowing flat access to all addresses
; @param[out] GS Selector allowing flat access to all addresses
; @param[out] SS Selector allowing flat access to all addresses
;
; @return None This routine jumps to SEC and does not return
;
Main16:
OneTimeCall EarlyInit16
其中 EarlyInit16在Init16.asm 中:
;
; Modified: EAX
;
; @param[in] EAX Initial value of the EAX register (BIST: Built-in Self Test)
; @param[out] ESP Initial value of the EAX register (BIST: Built-in Self Test)
;
EarlyInit16:
;
; ESP - Initial value of the EAX register (BIST: Built-in Self Test)
;
mov esp, eax
debugInitialize //这是空的宏
OneTimeCallRet EarlyInit16
返回 EarlyInit16 后继续:
;
; Transition the processor from 16-bit real mode to 32-bit flat mode
;
OneTimeCall TransitionFromReal16To32BitFlat
;
; Search for the Boot Firmware Volume (BFV)
;
OneTimeCall Flat32SearchForBfvBase
就是说,这亏啊显卡符合 IBM VGA 规范,而这份规范年代久远,资料比较难以找到【参考3】.大概是说显卡的一些基本显示参数(例如:分辨率)是通过IO和显卡沟通的。首先是定义了几个基本的文本显示模式,处于这种文本显示的模式下,直接对下面给出来的内存写入字符和参数(比如颜色,闪烁等等)即可显示出来,这也是很早之前我们使用的DOS 的显示方式。
apping of Display Memory into CPU Address Space The first element that defines this mapping is whether or not the VGA decodes accesses from the CPU. This is controlled by the RAM Enable field. If display memory decoding is disabled, then the VGA hardware ignores writes to its address space. The address range that the VGA hardware decodes is based upon the Memory Map Select field. The following table shows the address ranges in absolute 32-bit form decoded for each value of this field:
00 — A0000h-BFFFFh — 128K
01 — A0000h-AFFFFh — 64K
10 — B0000h-B7FFFh — 32K
11 — B8000h-BFFFFh — 32K
但是,我们的 UEFI 是工作在图形模式下。因此,BIOS需要先通过IO Port (例如:3CEh和3CFh,这些都是 VGA Spec 规定好的端口号)使得显卡切换到图形模式下。例如,下面就是Cirrus CLGD 5446显卡支持的显示模式:
1.前面提到的通过IO Port 对VGA 进行初始化的操作,在 \OvmfPkg\QemuVideoDxe\Gop.c文件中:
EFI_STATUS
EFIAPI
QemuVideoGraphicsOutputSetMode (
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
IN UINT32 ModeNumber
)
/*++
Routine Description:
Graphics Output protocol interface to set video mode
Arguments:
This - Protocol instance pointer.
ModeNumber - The mode number to be set.
Returns:
EFI_SUCCESS - Graphics mode was changed.
EFI_DEVICE_ERROR - The device had an error and could not complete the request.
EFI_UNSUPPORTED - ModeNumber is not supported by this device.
--*/
EFI_STATUS
EFIAPI
QemuVideoGraphicsOutputBlt (
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
IN UINTN SourceX,
IN UINTN SourceY,
IN UINTN DestinationX,
IN UINTN DestinationY,
IN UINTN Width,
IN UINTN Height,
IN UINTN Delta
)
其中调用了 FrameBufferBlt()
switch (BltOperation) {
case EfiBltVideoToBltBuffer:
case EfiBltBufferToVideo:
case EfiBltVideoFill:
case EfiBltVideoToVideo:
Status = FrameBufferBlt (
Private->FrameBufferBltConfigure,
BltBuffer,
BltOperation,
SourceX,
SourceY,
DestinationX,
DestinationY,
Width,
Height,
Delta
);
break;
/**
Performs a UEFI Graphics Output Protocol Blt operation.
@param[in] Configure Pointer to a configuration which was successfully
created by FrameBufferBltConfigure ().
@param[in,out] BltBuffer The data to transfer to screen.
@param[in] BltOperation The operation to perform.
@param[in] SourceX The X coordinate of the source for BltOperation.
@param[in] SourceY The Y coordinate of the source for BltOperation.
@param[in] DestinationX The X coordinate of the destination for
BltOperation.
@param[in] DestinationY The Y coordinate of the destination for
BltOperation.
@param[in] Width The width of a rectangle in the blt rectangle
in pixels.
@param[in] Height The height of a rectangle in the blt rectangle
in pixels.
@param[in] Delta Not used for EfiBltVideoFill and
EfiBltVideoToVideo operation. If a Delta of 0
is used, the entire BltBuffer will be operated
on. If a subrectangle of the BltBuffer is
used, then Delta represents the number of
bytes in a row of the BltBuffer.
@retval RETURN_INVALID_PARAMETER Invalid parameter were passed in.
@retval RETURN_SUCCESS The Blt operation was performed successfully.
**/
RETURN_STATUS
EFIAPI
FrameBufferBlt (
IN FRAME_BUFFER_CONFIGURE *Configure,
IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
IN UINTN SourceX,
IN UINTN SourceY,
IN UINTN DestinationX,
IN UINTN DestinationY,
IN UINTN Width,
IN UINTN Height,
IN UINTN Delta
)
/**
Performs a UEFI Graphics Output Protocol Blt Video Fill.
@param[in] Configure Pointer to a configuration which was successfully
created by FrameBufferBltConfigure ().
@param[in] Color Color to fill the region with.
@param[in] DestinationX X location to start fill operation.
@param[in] DestinationY Y location to start fill operation.
@param[in] Width Width (in pixels) to fill.
@param[in] Height Height to fill.
@retval RETURN_INVALID_PARAMETER Invalid parameter was passed in.
@retval RETURN_SUCCESS The video was filled successfully.
**/
EFI_STATUS
FrameBufferBltLibVideoFill (
IN FRAME_BUFFER_CONFIGURE *Configure,
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Color,
IN UINTN DestinationX,
IN UINTN DestinationY,
IN UINTN Width,
IN UINTN Height
)
{}
#if CONFIG_IDF_TARGET_ESP32C3
#define FSPI 0
#define HSPI 1
#else
#define FSPI 1 //SPI bus attached to the flash (can use the same data lines but different SS)
#define HSPI 2 //SPI bus normally mapped to pins 12 - 15, but can be matrixed to any pins
#if CONFIG_IDF_TARGET_ESP32
#define VSPI 3 //SPI bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins
#endif
#endif
#include <SPI.h>
static const int spiClk = 40000000;
SPIClass * hspi = NULL;
void setup() {
Serial.begin(115200);
//initialise two instances of the SPIClass attached to VSPI and HSPI respectively
hspi = new SPIClass(HSPI);
//initialise hspi with default pins
//SCLK = 14, MISO = 12, MOSI = 13, SS = 15
hspi->begin(10,12,11,13);
//set up slave select pins as outputs as the Arduino API
//doesn't handle automatically pulling SS low
pinMode(hspi->pinSS(), OUTPUT); //HSPI SS
}
// the loop function runs over and over again until power down or reset
void loop() {
spiCommand(hspi, 0b11001100);
Serial.print(MISO);Serial.print(" ");
Serial.print(MOSI);Serial.print(" ");
Serial.print(SCK);Serial.print(" ");
Serial.print(SS);Serial.print(" ");
Serial.print(HSPI);Serial.print(" ");
Serial.print(FSPI);Serial.print(" ");
Serial.print(hspi->pinSS());Serial.println(" ");
delay(2000);
}
void spiCommand(SPIClass *spi, byte data) {
//use it as you would the regular arduino SPI API
spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(spi->pinSS(), LOW); //pull SS slow to prep other end for transfer
spi->transfer(data);
digitalWrite(spi->pinSS(), HIGH); //pull ss high to signify end of data transfer
spi->endTransaction();
}
#include <SPI.h>
static const int spiClk = 40000000;
void setup() {
Serial.begin(115200);
SPI.begin();
}
void loop() {
spiCommand(&SPI, 0b11001100);
Serial.print(MISO);Serial.print(" ");
Serial.print(MOSI);Serial.print(" ");
Serial.print(SCK);Serial.print(" ");
Serial.print(SS);Serial.print(" ");
Serial.print(HSPI);Serial.print(" ");
Serial.print(FSPI);Serial.println(" ");
delay(200);
}
void spiCommand(SPIClass *spi, byte data) {
//use it as you would the regular arduino SPI API
spi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
digitalWrite(spi->pinSS(), LOW); //pull SS slow to prep other end for transfer
spi->transfer(data);
digitalWrite(spi->pinSS(), HIGH); //pull ss high to signify end of data transfer
spi->endTransaction();
}