一年多以前,提出了个奇怪的想法:是否可以在自己编写的Application中输出到 NT32 的模拟器LOG中?当时遇到的问题是,如果想直接输出必须调用 WinNtThunkDxe 这样的Protocol,而在定义Protocol的时候必须使用Windows.h 的头文件,但是 AppPkg 中无法做到这一点,最终找到的解决方法是修改gST->Reset 。但是很显然,这并非完美的解决方法【参考1】。
最近,忽然想起来,可以重新定义一个自己的 Protocol 头,只要需要用到的函数偏移正确,调用时放入正确的参数同样能正常工作。需要用到的最重要的 Protocol 结构体在 \UDK2017\Nt32Pkg\Include\Protocol\WinNtThunk.h :
typedef struct { UINT64 Signature; // // Win32 Process APIs // WinNtGetProcAddress GetProcAddress; WinNtGetTickCount GetTickCount; WinNtLoadLibraryEx LoadLibraryEx; WinNtFreeLibrary FreeLibrary; WinNtSetPriorityClass SetPriorityClass; WinNtSetThreadPriority SetThreadPriority; WinNtSleep Sleep; WinNtSuspendThread SuspendThread; WinNtGetCurrentThread GetCurrentThread; WinNtGetCurrentThreadId GetCurrentThreadId; WinNtGetCurrentProcess GetCurrentProcess; WinNtCreateThread CreateThread; WinNtTerminateThread TerminateThread; WinNtSendMessage SendMessage; WinNtExitThread ExitThread; WinNtResumeThread ResumeThread; WinNtDuplicateHandle DuplicateHandle; // // Wint32 Mutex primitive // WinNtInitializeCriticalSection InitializeCriticalSection; WinNtEnterCriticalSection EnterCriticalSection; WinNtLeaveCriticalSection LeaveCriticalSection; WinNtDeleteCriticalSection DeleteCriticalSection; WinNtTlsAlloc TlsAlloc; WinNtTlsFree TlsFree; WinNtTlsSetValue TlsSetValue; WinNtTlsGetValue TlsGetValue; WinNtCreateSemaphore CreateSemaphore; WinNtWaitForSingleObject WaitForSingleObject; WinNtReleaseSemaphore ReleaseSemaphore; // // Win32 Console APIs // WinNtCreateConsoleScreenBuffer CreateConsoleScreenBuffer; WinNtFillConsoleOutputAttribute FillConsoleOutputAttribute; WinNtFillConsoleOutputCharacter FillConsoleOutputCharacter; WinNtGetConsoleCursorInfo GetConsoleCursorInfo; WinNtGetNumberOfConsoleInputEvents GetNumberOfConsoleInputEvents; WinNtPeekConsoleInput PeekConsoleInput; WinNtScrollConsoleScreenBuffer ScrollConsoleScreenBuffer; WinNtReadConsoleInput ReadConsoleInput; WinNtSetConsoleActiveScreenBuffer SetConsoleActiveScreenBuffer; WinNtSetConsoleCursorInfo SetConsoleCursorInfo; WinNtSetConsoleCursorPosition SetConsoleCursorPosition; WinNtSetConsoleScreenBufferSize SetConsoleScreenBufferSize; WinNtSetConsoleTitleW SetConsoleTitleW; WinNtWriteConsoleInput WriteConsoleInput; WinNtWriteConsoleOutput WriteConsoleOutput; // // Win32 File APIs // WinNtCreateFile CreateFile; WinNtDeviceIoControl DeviceIoControl; WinNtCreateDirectory CreateDirectory; WinNtRemoveDirectory RemoveDirectory; WinNtGetFileAttributes GetFileAttributes; WinNtSetFileAttributes SetFileAttributes; WinNtCreateFileMapping CreateFileMapping; WinNtCloseHandle CloseHandle; WinNtDeleteFile DeleteFile; WinNtFindFirstFile FindFirstFile; WinNtFindNextFile FindNextFile; WinNtFindClose FindClose; WinNtFlushFileBuffers FlushFileBuffers; WinNtGetEnvironmentVariable GetEnvironmentVariable; WinNtGetLastError GetLastError; WinNtSetErrorMode SetErrorMode; WinNtGetStdHandle GetStdHandle; WinNtMapViewOfFileEx MapViewOfFileEx; WinNtReadFile ReadFile; WinNtSetEndOfFile SetEndOfFile; WinNtSetFilePointer SetFilePointer; WinNtWriteFile WriteFile; WinNtGetFileInformationByHandle GetFileInformationByHandle; WinNtGetDiskFreeSpace GetDiskFreeSpace; WinNtGetDiskFreeSpaceEx GetDiskFreeSpaceEx; WinNtMoveFile MoveFile; WinNtSetFileTime SetFileTime; WinNtSystemTimeToFileTime SystemTimeToFileTime; // // Win32 Time APIs // WinNtLocalFileTimeToFileTime LocalFileTimeToFileTime; WinNtFileTimeToLocalFileTime FileTimeToLocalFileTime; WinNtFileTimeToSystemTime FileTimeToSystemTime; WinNtGetSystemTime GetSystemTime; WinNtSetSystemTime SetSystemTime; WinNtGetLocalTime GetLocalTime; WinNtSetLocalTime SetLocalTime; WinNtGetTimeZoneInformation GetTimeZoneInformation; WinNtSetTimeZoneInformation SetTimeZoneInformation; WinNttimeSetEvent timeSetEvent; WinNttimeKillEvent timeKillEvent; // // Win32 Serial APIs // WinNtClearCommError ClearCommError; WinNtEscapeCommFunction EscapeCommFunction; WinNtGetCommModemStatus GetCommModemStatus; WinNtGetCommState GetCommState; WinNtSetCommState SetCommState; WinNtPurgeComm PurgeComm; WinNtSetCommTimeouts SetCommTimeouts; WinNtExitProcess ExitProcess; WinNtSprintf SPrintf; WinNtGetDesktopWindow GetDesktopWindow; WinNtGetForegroundWindow GetForegroundWindow; WinNtCreateWindowEx CreateWindowEx; WinNtShowWindow ShowWindow; WinNtUpdateWindow UpdateWindow; WinNtDestroyWindow DestroyWindow; WinNtInvalidateRect InvalidateRect; WinNtGetWindowDC GetWindowDC; WinNtGetClientRect GetClientRect; WinNtAdjustWindowRect AdjustWindowRect; WinNtSetDIBitsToDevice SetDIBitsToDevice; WinNtBitBlt BitBlt; WinNtGetDC GetDC; WinNtReleaseDC ReleaseDC; WinNtRegisterClassEx RegisterClassEx; WinNtUnregisterClass UnregisterClass; WinNtBeginPaint BeginPaint; WinNtEndPaint EndPaint; WinNtPostQuitMessage PostQuitMessage; WinNtDefWindowProc DefWindowProc; WinNtLoadIcon LoadIcon; WinNtLoadCursor LoadCursor; WinNtGetStockObject GetStockObject; WinNtSetViewportOrgEx SetViewportOrgEx; WinNtSetWindowOrgEx SetWindowOrgEx; WinNtMoveWindow MoveWindow; WinNtGetWindowRect GetWindowRect; WinNtGetMessage GetMessage; WinNtTranslateMessage TranslateMessage; WinNtDispatchMessage DispatchMessage; WinNtGetProcessHeap GetProcessHeap; WinNtHeapAlloc HeapAlloc; WinNtHeapFree HeapFree; WinNtQueryPerformanceCounter QueryPerformanceCounter; WinNtQueryPerformanceFrequency QueryPerformanceFrequency; } EFI_WIN_NT_THUNK_PROTOCOL;
修改之后的可以直接在 UEFI Application 中进行定义的结构体如下
typedef struct { UINT64 Signature; // // Win32 Process APIs // UINTN API1[17]; // // Wint32 Mutex primitive // UINTN API2[11]; // // Win32 Console APIs // UINTN API3[15]; // // Win32 File APIs // UINTN API41[16]; MyWinNtGetStdHandle GetStdHandle; UINTN API42[4]; MyWinNtWriteFile WriteFile; UINTN API5[6]; // // Win32 Time APIs // UINTN API6[10]; // // Win32 Serial APIs // UINTN API7[44]; } MY_EFI_WIN_NT_THUNK_PROTOCOL;
这个结构体中,对我们有用的是 WinNtWriteFile WriteFile 还有 WinNtGetStdHandle GetStdHandle。只要这两个的偏移正确即可进行调用。对于这两个函数,我们还需要重新改写一下原型,比如之前的定义为:
typedef WINBASEAPI BOOL (WINAPI *WinNtWriteFile) ( HANDLE FileHandle, LPCVOID Buffer, DWORD NumberOfBytesToWrite, LPDWORD NumberOfBytesWritten, LPOVERLAPPED Overlapped );
经过修改之后的如下
typedef EFI_STATUS (EFIAPI *MyWinNtWriteFile) ( IN EFI_HANDLE FileHandle, CHAR8* Buffer, UINT32 NumberOfBytesToWrite, UINT32* NumberOfBytesWritten, UINT32 Overlapped );
就是这样,只要我们定义出正确的函数偏移,再喂给他正确的参数就能够完成调用。最后,完整的代码如下:
/** @file A simple, basic, application showing how the Hello application could be built using the "Standard C Libraries" from StdLib. Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license. THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include <Library/BaseLib.h> #include <Uefi.h> #include <Library/UefiLib.h> #include <Library/PrintLib.h> #include <Library/ShellCEntryLib.h> #define STD_OUTPUT_HANDLE -11 extern EFI_BOOT_SERVICES *gBS; EFI_GUID gEfiWinNtThunkProtocolGuid = { 0x58C518B1, 0x76F3, 0x11D4, { 0xBC, 0xEA, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }}; typedef EFI_STATUS (EFIAPI *MyWinNtWriteFile) ( IN EFI_HANDLE FileHandle, CHAR8* Buffer, UINT32 NumberOfBytesToWrite, UINT32* NumberOfBytesWritten, UINT32 Overlapped ); typedef EFI_HANDLE (EFIAPI *MyWinNtGetStdHandle) ( EFI_HANDLE StdHandle ); typedef struct { UINT64 Signature; // // Win32 Process APIs // UINTN API1[17]; // // Wint32 Mutex primitive // UINTN API2[11]; // // Win32 Console APIs // UINTN API3[15]; // // Win32 File APIs // UINTN API41[16]; MyWinNtGetStdHandle GetStdHandle; UINTN API42[4]; MyWinNtWriteFile WriteFile; UINTN API5[6]; // // Win32 Time APIs // UINTN API6[10]; // // Win32 Serial APIs // UINTN API7[44]; } MY_EFI_WIN_NT_THUNK_PROTOCOL; /*** Demonstrates basic workings of the main() function by displaying a welcoming message. Note that the UEFI command line is composed of 16-bit UCS2 wide characters. The easiest way to access the command line parameters is to cast Argv as: wchar_t **wArgv = (wchar_t **)Argv; @param[in] Argc Number of argument tokens pointed to by Argv. @param[in] Argv Array of Argc pointers to command line tokens. @retval 0 The application exited normally. @retval Other An error occurred. ***/ int main ( IN int Argc, IN char **Argv ) { EFI_STATUS Status; MY_EFI_WIN_NT_THUNK_PROTOCOL *MyWinNTThunkProtocol; // // Cache of standard output handle . // EFI_HANDLE mStdOut; CHAR8 Buffer[200]; UINT32 CharCount; // // Look for Ram Disk Protocol // Status = gBS->LocateProtocol ( &gEfiWinNtThunkProtocolGuid, NULL, (VOID **)&MyWinNTThunkProtocol ); if (EFI_ERROR (Status)) { Print(L"Couldn't find WinNtThunkProtocol\n"); return EFI_ALREADY_STARTED; } Print(L"Found WinNt Thunk\n"); // // Cache standard output handle. // mStdOut = MyWinNTThunkProtocol-> GetStdHandle ((EFI_HANDLE)STD_OUTPUT_HANDLE); Print(L"mStdOut=%X\n",mStdOut); CharCount = (UINT32)AsciiSPrint ( Buffer, sizeof (Buffer), "www.lab-z.com %X\n\r", 2017 ); // // Callout to standard output. // MyWinNTThunkProtocol->WriteFile ( mStdOut, Buffer, CharCount, &CharCount, 0 ); return EFI_SUCCESS; }
我们在 NT32 的Shell中调用编写好的 Application 2次:
参考:
- http://www.lab-z.com/stu82/ NT32Pkg的Debug Message
2018年1月4日 krishnaLee
1,在Nt32Pkg.dsc的【component】区域写:
SampleAppDebug/SampleApp.inf {
gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xff
gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0xffffffff
}
2,编译:build -p Nt32Pkg\Nt32Pkg.dsc -m SampleAppDebug\SampleApp.inf
3,运行。
附件是例子的 application: