之前的一篇文章《Step to UEFI (9)—-使用RDTSC计算当前CPU 频率》【参考1】给出了一个计算当前CPU频率的方法。不过 Tim 给我留言,他说这篇文章的程序无法在 x64下正常编译:

我猜测原因是因为我的程序使用的内嵌汇编,内嵌汇编无法被X64的编译器正常编译的。关于这个说法可以看【参考2】。
动手在 UDK2014 下面实验,不过在看到描述的问题之前还是要走一段路的。
使用编译命令 build -a X64 -p AppPkg\AppPkg.dsc 得到下面的错误信息
c:\edk\AppPkg\AppPkg.dsc(94): error 000E: File/directory not found in workspace
c:\edk\PerformancePkg\Library\DxeTscTimerLib\DxeTscTimerLib.inf
检查 AppPkg.dsc
修改
[LibraryClasses.X64]
TimerLib|PerformancePkg/Library/DxeTscTimerLib/DxeTscTimerLib.inf
修改为
[LibraryClasses.X64]
TimerLib|PerformancePkg/Library/TscTimerLib/DxeTscTimerLib.inf
再次编译,有下面的错误信息
Processing meta-data .. done!
Building ... c:\edk\MdePkg\Library\BaseLib\BaseLib.inf [X64]
"C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe" /Foc:\edk\Build\AppPkg\DEBUG_MYTOOLS\X64\MdePkg\Library\BaseLib\BaseLib\OUTPUT\.\CheckSum.obj /nologo /c /WX /GS- /W4 /Gs32768 /Gy /D UNICODE /O1ib2 /GL /FIAutoG
en.h /EHs-c- /GR- /GF /Zi /Gm /X /Zc:wchar_t /X /Zc:wchar_t /GL- /Ic:\edk\MdePkg\Library\BaseLib\X64 /Ic:\edk\MdePkg\Library\BaseLib /Ic:\edk\Build\AppPkg\DEBUG_MYTOOLS\X64\MdePkg\Library\BaseLib\BaseLib\DEBUG /Ic:\edk\MdePkg /Ic:\edk\M
dePkg\Include /Ic:\edk\MdePkg\Include\X64 c:\edk\MdePkg\Library\BaseLib\CheckSum.c
'C:\Program' 不是内部或外部命令,也不是可运行的程序或批处理文件。
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe' : return code '0x1' Stop.
build...
: error 7000: Failed to execute command
C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\nmake.exe /nologo tbuild
build...
: error 7000: Failed to execute command
C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\nmake.exe /nologo tbuild
build...
: error F002: Failed to build module
c:\edk\MdePkg\Library\BaseLib\BaseLib.inf [X64, MYTOOLS, DEBUG]
- Failed -
Build end time: 11:22:09, Mar.09 2015
Build total time: 00:00:03
尝试在命令行窗口直接运行”C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe” 错误提示是:
C:\EDK>"C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe"
系统找不到指定的路径。
这是因为目前的编译环境缺少 64位编译器。我使用的是 vs2008 express 版本,默认是没有 64位的cl.exe的。
找到 ‘x86_amd64’ 拷贝到 “C:\Program Files\Microsoft Visual Studio 9.0\VC\bin”
需要的朋友可以在这里下载到 x86_amd64
再次编译,然后就能看到 Tim 说的问题啦
C:\Program Files\Microsoft Visual Studio 9.0\VC\bin
Building ... c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.inf [X64]
"C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe" /Foc:\edk\Build\AppPkg\DEBUG_MYTOOLS\X64\AppPkg\Applications\FreqCalc\FreqCalc\OUTPUT\.\FreqCalc.obj /nologo /c /WX /GS- /W4 /Gs32768 /Gy /D UNICODE /O1ib2 /GL /
FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm /X /Zc:wchar_t /Ic:\edk\AppPkg\Applications\FreqCalc /Ic:\edk\Build\AppPkg\DEBUG_MYTOOLS\X64\AppPkg\Applications\FreqCalc\FreqCalc\DEBUG /Ic:\edk\MdePkg /Ic:\edk\MdePkg\Include /Ic:\edk\MdePkg\Includ
e\X64 /Ic:\edk\ShellPkg /Ic:\edk\ShellPkg\Include /Ic:\edk\MdeModulePkg /Ic:\edk\MdeModulePkg\Include c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c
FreqCalc.c
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(15) : error C4235: nonstandard extension used : '__asm' keyword not supported on this architecture
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2146: syntax error : missing ';' before identifier 'mov'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : warning C4550: expression evaluates to a function which is missing an argument list
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2065: 'mov' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2146: syntax error : missing ';' before identifier 'dword'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2065: 'dword' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2146: syntax error : missing ';' before identifier 'ptr'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2065: 'ptr' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(18) : error C2146: syntax error : missing ';' before identifier 'value'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2065: 'eax' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2146: syntax error : missing ';' before identifier 'mov'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2065: 'mov' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2146: syntax error : missing ';' before identifier 'dword'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2065: 'dword' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2146: syntax error : missing ';' before identifier 'ptr'
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2065: 'ptr' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(19) : error C2109: subscript requires array or pointer type
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(20) : error C2065: 'edx' : undeclared identifier
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.c(20) : error C2143: syntax error : missing ';' before '}'
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\x86_amd64\cl.exe"' : return code '0x2'
Stop.
build...
: error 7000: Failed to execute command
C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\nmake.exe /nologo tbuild
build...
: error F002: Failed to build module
c:\edk\AppPkg\Applications\FreqCalc\FreqCalc.inf [X64, MYTOOLS, DEBUG]
- Failed -
Build end time: 11:26:31, Mar.09 2015
Build total time: 00:01:16
然后就考虑如何解决,网上搜索了一下,有人遇到同样的问题【参考3】,具体的解决方法在【参考4】
简单的说,就是单独写一个 asm 然后在对应的 Inf中声明一下即可。
针对我遇到的问题,程序如下
FreqCalc.inf
## @file
# Sample UEFI Application Reference EDKII Module
#
# This is a sample shell application that will print "UEFI Hello World!" to the
# UEFI Console based on PCD setting.
#
# It demos how to use EDKII PCD mechanism to make code more flexible.
#
# Copyright (c) 2008 - 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.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.
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = FreqCalc
FILE_GUID = 6987936E-ED34-44db-AE97-2FA5E4ED2216
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
[Sources]
FreqCalc.c
ReadTsc1.asm
[Packages]
MdePkg/MdePkg.dec
ShellPkg/ShellPkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
PcdLib
ShellCEntryLib
ShellLib
[FeaturePcd]
[Pcd]
然后在FreqCalc.c 写成下面这样,特别注意要声明一下你用的那个汇编语言中的函数名
//
// FreqCalc.C
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
EFI_SYSTEM_TABLE *gST;
EFI_BOOT_SERVICES *gBS;
UINT64
EFIAPI
zReadTsc (
VOID
);
//
// Entry point function - ShowVersion
//
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT64 elsp;
gST = SystemTable;
gBS = SystemTable->BootServices;
elsp=zReadTsc();
gBS -> Stall(1000000);
Print(L"CPU Frequency: %ld \n",zReadTsc() - elsp);
return EFI_SUCCESS;
}
然后还有一个ReadTsc1.asm (其实 uefi 里面提供了一个 x64的 ReadTsc.asm,可以直接使用,这里是为了让读者看的更清楚,取了一个不会重复的名称)
.code
;------------------------------------------------------------------------------
; UINT64
; EFIAPI
; zReadTsc (
; VOID
; );
;------------------------------------------------------------------------------
zReadTsc PROC
rdtsc
shl rdx, 20h
or rax, rdx
ret
zReadTsc ENDP
END
接下来就可以正常编译了,生成的 efi 文件会比 x64的大一些。然后我在实体机的 x64 shell下面运行成功。就是说上面的方法是没问题的。
FreqCalc
参考:
1.http://www.lab-z.com/rdtsc/ Step to UEFI (9)—-使用RDTSC计算当前CPU 频率
2.http://stackoverflow.com/questions/1295452/why-does-msvc-not-support-inline-assembly-for-amd64-and-itanium-targets 简单解释,说x64下无法支持 _ASM汇编
3.http://biosren.com/viewthread.php?tid=6822&highlight=%C4%DA%C7%B6%2B%BB%E3%B1%E0 請問如何在EDK2的AppPkg裡面使用asm (有人问同样的问题)
4.http://www.biosren.com/thread-6632-1-1.html 給個UEFI 內嵌彙編的小程序吧? 本文的主要参考,其中介绍了如何写单独写一个ASM文件