Step to UEFI (7)—-加速 UEFI 模拟环境的启动

前面的例子都是在 NT32 模拟环境中运行的,在启动这个环境的时候会有一个5秒的滚动条。为了更爽快一些,可以将这个滚动条设置为一闪而过,马上进入Shell环境。“以文找文”,搜索“Start showing progress bar”字样,确定下面的代码显示这个字符串:

\IntelFrameworkModulePkg\Universal\BdsDxe\FrontPage.c (Experiment)

/**

  Function show progress bar to wait for user input.

  @param TimeoutDefault  - The fault time out value before the system

                         continue to boot.

  @retval  EFI_SUCCESS       User pressed some key except "Enter"

  @retval  EFI_TIME_OUT      Timout expired or user press "Enter"

**/

EFI_STATUS

ShowProgress (

  IN UINT16                       TimeoutDefault

  )

{

  EFI_STATUS                    Status;

  CHAR16                        *TmpStr;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color;

  EFI_INPUT_KEY                 Key;

  UINT16                        TimeoutRemain;

  if (TimeoutDefault == 0) {

    return EFI_TIMEOUT;

  }

  DEBUG ((EFI_D_INFO, "\n\nStart showing progress bar... Press any key to stop it! .\n"));

  SetMem (&Foreground, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);

  SetMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);

  SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);

  //

  // Clear the progress status bar first

  //

  TmpStr = GetStringById (STRING_TOKEN (STR_START_BOOT_OPTION));

  if (TmpStr != NULL) {

    PlatformBdsShowProgress (Foreground, Background, TmpStr, Color, 0, 0);

  }

 

 

追踪发现是下面的代码调用到上面的  \IntelFrameworkModulePkg\Universal\BdsDxe\FrontPage.c (Experiment)

/**

  This function is the main entry of the platform setup entry.

  The function will present the main menu of the system setup,

  this is the platform reference part and can be customize.

  @param TimeoutDefault     The fault time out value before the system

                            continue to boot.

  @param ConnectAllHappened The indicater to check if the connect all have

                            already happened.

**/

VOID

PlatformBdsEnterFrontPage (

  IN UINT16                       TimeoutDefault,

  IN BOOLEAN                      ConnectAllHappened

  )

…………………………

…………………………

  HotkeyBoot ();

  if (TimeoutDefault != 0xffff) {

    Status = ShowProgress (TimeoutDefault);

    StatusHotkey = HotkeyBoot ();

 

上面的代码又是下面调用到的

  //

  // Init the time out value

  //

  Timeout = PcdGet16 (PcdPlatformBootTimeOut);

 

 

最后确定参数是来自 PCD ,修改 \Nt32Pkg\Nt32Pkg.dsc

[PcdsDynamicHii.common.DEFAULT]

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdSetupConOutColumn|L”SetupConsoleConfig”|gEfiGlobalVariableGuid|0x0|80

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdSetupConOutRow|L”SetupConsoleConfig”|gEfiGlobalVariableGuid|0x4|25

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L”Timeout”|gEfiGlobalVariableGuid|0x0|10

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdHardwareErrorRecordLevel|L”HwErrRecSupport”|gEfiGlobalVariableGuid|0x0|1

将上面红色标记的修改为你希望的即可.

这里是一个修改为20秒的例子

Step2UEFI7

Step to UEFI Shell (6) —- Shell 中使用 Float

如果你在Shell下直接使用 Float 或者 Double 在编译的时候会遇到这样的问题

Architecture(s) = IA32
Build target = DEBUG
Toolchain = VS2008

Active Platform = c:\edk2\Nt32Pkg\Nt32Pkg.dsc
Flash Image Definition = c:\edk2\Nt32Pkg\Nt32Pkg.fdf

Processing meta-data … done!
“C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\link.exe” /out:”c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.exe” /base:0x10000000 /pdb:”c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.pdb” /LIBPATH:”C:\Program Files\Microsoft Visual Studio 9.0\VC\Lib” /LIBPATH:”C:\Program Files\Microsoft Visual Studio 9.0\VC\PlatformSdk\Lib” /NOLOGO /SUBSYSTEM:CONSOLE /NODEFAULTLIB /IGNORE:4086 /MAP /OPT:REF /DEBUG /MACHINE:I386 /LTCG Kernel32.lib MSVCRTD.lib Gdi32.lib User32.lib Winmm.lib Advapi32.lib /EXPORT:InitializeDriver=_ModuleEntryPoint /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE @c:\edk2\Build\NT32\DEBUG_VS2008\IA32\Nt32Pkg\Sec\SecMain\OUTPUT\static_library_files.lst
LINK : warning LNK4108: /ALIGN specified without /DRIVER; image may not run
LINK : warning LNK4001: no object files specified; libraries used
Creating library c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.lib and object c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.exp
Generating code
Finished generating code
“C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\link.exe” /OUT:c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG /EXPORT:InitializeDriver=_ModuleEntryPoint /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE @c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\OUTPUT\static_library_files.lst
Creating library c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.lib and object c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.exp
Generating code
Finished generating code
c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.dll : warning LNK4086: entrypoint ‘__ModuleEntryPoint’ is not __stdcall with 12 bytes of arguments; image may not run
FloatTest.lib(floattest.obj) : error LNK2001: unresolved external symbol __fltused
c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.dll : fatal error LNK1120: 1 unresolved externals

我baidu和google之后得到的大概的结论是:UEFI 替换了Link中的默认库(CRT.LIB?),但是因为MS的编译器存在一个bug,所以导致仍然使用部分的默认库,所以导致这样的问题【这个解释存疑,不确定】。至于解决方法,经过我的实验确认就是使用 EADK 库【参考1】。我的理解是这个库是用来替代标准C库编写EDKII程序的,因此Float之类在其中也有重新定义。

加入方法:

1.解压EADK,然后将其中三个目录放到你EDK2的根目录下

2.修改 Nt32Pkg.dsc 加入红色部分

#
# Misc
#
DebugLib|IntelFrameworkModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
DebugPrintErrorLevelLib|MdeModulePkg/Library/DxeDebugPrintErrorLevelLib/DxeDebugPrintErrorLevelLib.inf
PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
#LABZ_Start
SortLib|ShellPkg/Library/UefiSortLib/UefiSortLib.inf
FileHandleLib|ShellPkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
LibC|StdLib/LibC/LibC.inf
LibStdLib|StdLib/LibC/StdLib/StdLib.inf
LibString|StdLib/LibC/String/String.inf
LibWchar|StdLib/LibC/Wchar/Wchar.inf
LibCType|StdLib/LibC/Ctype/Ctype.inf
LibTime|StdLib/LibC/Time/Time.inf
LibStdio|StdLib/LibC/Stdio/Stdio.inf
LibGdtoa|StdLib/LibC/gdtoa/gdtoa.inf
LibLocale|StdLib/LibC/Locale/Locale.inf
LibUefi|StdLib/LibC/Uefi/Uefi.inf
LibMath|StdLib/LibC/Math/Math.inf
LibSignal|StdLib/LibC/Signal/Signal.inf
LibNetUtil|StdLib/LibC/NetUtil/NetUtil.inf
#LABZ_End
[LibraryClasses.common.USER_DEFINED]
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
ReportStatusCodeLib|MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
OemHookStatusCodeLib|Nt32Pkg/Library/PeiNt32OemHookStatusCodeLib/PeiNt32OemHookStatusCodeLib.inf
MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

3. floattest.inf 中必须加入下面2个

[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
PcdLib
ShellCEntryLib
ShellLib
LibMath
LibC|

下面就可以在你的代码中放心大胆的使用Float了。

一个计算 Pi 的Shell Application

#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>

//
// Based on code found at http://code.google.com/p/my-itoa/
//
int 
Integer2AsciiString(int val, char* buf)
{
    const unsigned int radix = 10;

    char* p = buf;
    unsigned int a; 
    int len;
    char* b;
    char temp;
    unsigned int u;

    if (val < 0) {
        *p++ = '-';
        val = 0 - val;
    }
    u = (unsigned int)val;
    b = p;

    do {
        a = u % radix;
        u /= radix;
        *p++ =(char) a + '0';
    } while (u > 0);

    len = (int)(p - buf);
    *p-- = 0;

    // swap 
    do {
       temp = *p; *p = *b; *b = temp;
       --p; ++b;
    } while (b < p);

    return len;
}

//
// Based on code found on the Internet (author unknown)
// Search for ftoa implementations
//
int 
Float2AsciiString(float f, char *buffer, int numdecimals)
{
    int status = 0;
    char *s = buffer;
    long mantissa, int_part, frac_part;
    short exp2;
    char m;

    typedef union {
        long L;
        float F;
    } LF_t;
    LF_t x;

    if (f == 0.0) {           // return 0.00
        *s++ = '0'; *s++ = '.'; *s++ = '0'; *s++ = '0'; 
        *s = 0;
       return status;
    }

    x.F = f;

    exp2 = (unsigned char)(x.L >> 23) - 127;
    mantissa = (x.L & 0xFFFFFF) | 0x800000;
    frac_part = 0;
    int_part = 0;

    if (exp2 >= 31 || exp2 < -23) {
        *s = 0;
        return 1;
    } 

    if (exp2 >= 0) {
        int_part = mantissa >> (23 - exp2);
        frac_part = (mantissa << (exp2 + 1)) & 0xFFFFFF;
    } else {
        frac_part = (mantissa & 0xFFFFFF) >> -(exp2 + 1);
    }

    if (int_part == 0)
       *s++ = '0';
    else {
        Integer2AsciiString(int_part, s);
        while (*s) s++;
    }
    *s++ = '.';

    if (frac_part == 0)
        *s++ = '0';
    else {
        for (m = 0; m < numdecimals; m++) {                       // print BCD
            frac_part = (frac_part << 3) + (frac_part << 1);      // frac_part *= 10
            *s++ = (frac_part >> 24) + '0';
            frac_part &= 0xFFFFFF;
        }
    }
    *s = 0;

    return status;
}

VOID
Ascii2UnicodeString(CHAR8 *String, CHAR16 *UniString)
{
    while (*String != '\0') {
        *(UniString++) = (CHAR16) *(String++);
    }
    *UniString = '\0';
}

//PI= 4- /3+4/5-4/7+4/9
EFI_STATUS 
EFIAPI
CalcPI()
{
    char str[8];
    static CHAR16 wstr[8];
    int i;

    float g1 = (float) 1;
    float g2 = (float) 0;

    for (i=0;i<100;i++)
     {
	if (i%2==0) {g2=g2+ 4/ g1;}
        else {g2=g2-4/ g1;}
	Float2AsciiString(g2, str, 4);         
	Ascii2UnicodeString(str, wstr);
	Print(L" PI: %s\n", wstr);
        g1=g1+2;
     }

    return EFI_SUCCESS;
}

EFI_STATUS 
EFIAPI
UefiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{

    CalcPI();

    return EFI_SUCCESS;
}

运行结果:

floattest

示例代码下载 floattest

参考:

1.http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=EDKII_EADK

EDK II Application Development Kit for include the Standard C Libraries in UEFI Shell Applications

 

Step to UEFI Shell (5)—-获得Shell下内存分配状况

在Shell下面可以使用 memmap 命令查看到当前的内存状况:

step51

UEFI下面没有了E820 Table。取而代之的是GetMemoryMap()。UEFI spec Version 2.4 中 P143有如下的描述,实际上是一个长得和E820很像的东西。

step52

在网上可以搜索到一个示例[参考1],原文是用Toolkit 写成的,经过一些改造成为可以直接在EDK2下面的编译的代码。

GetMap.c:

#include <Uefi.h>
#include <Library/PcdLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>

#define PAGE_SIZE 4096 

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", 
    L"EfiMaxMemoryType"
}; 

EFI_BOOT_SERVICES *gBS;  
const CHAR16 * 
memory_type_to_str(UINT32 type) 
{ 
    if (type > sizeof(memory_types)/sizeof(CHAR16 *)) 
        return L"Unknown"; 

    return memory_types[type]; 
} 

EFI_STATUS 
memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, 
           UINTN *map_key, UINTN *desc_size, UINT32 *desc_version) 
{ 
    EFI_STATUS err = EFI_SUCCESS; 

    *map_size = sizeof(**map_buf) * 31; 

get_map: 
    *map_size += sizeof(**map_buf); 

    err = gBS->AllocatePool(EfiLoaderData, *map_size, (void **)map_buf); 
    if (err != EFI_SUCCESS) { 
        Print(L"ERROR: Failed to allocate pool for memory map"); 
        return err; 
    } 

    err = gBS->GetMemoryMap(map_size, *map_buf, map_key, desc_size, desc_version); 
    if (err != EFI_SUCCESS) { 
        if (err == EFI_BUFFER_TOO_SMALL) { 
            gBS->FreePool((void *)*map_buf); 
            goto get_map; 
        } 
        Print(L"ERROR: Failed to get memory map"); 
    } 
    return err; 
} 

GetMap.inf

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = GetMap
  FILE_GUID                      = 6987936E-ED34-44db-AE97-1FA5E4ED2318
  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]
  GetMap.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec
  MdeModulePkg/MdeModulePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  PcdLib
  ShellCEntryLib
  ShellLib

[FeaturePcd]
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable

[Pcd]
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString || gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable  ## Valid when gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes  || gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable  ## Valid when gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable

EFI_STATUS  
print_memory_map(void) 
{ 
    EFI_MEMORY_DESCRIPTOR *buf; 
    UINTN desc_size; 
    UINT32 desc_version; 
    UINTN size, map_key, mapping_size; 
    EFI_MEMORY_DESCRIPTOR *desc; 
    EFI_STATUS err = EFI_SUCCESS; 
    int i = 0; 

    err = memory_map(&buf, &size, &map_key, &desc_size, &desc_version); 
    if (err != EFI_SUCCESS) 
        return err; 

    Print(L"Memory Map Size: %d\n", size); 
    Print(L"Map Key: %d\n", map_key); 
    Print(L"Descriptor Version: %d\n", desc_version); 
    Print(L"Descriptor Size: %d\n\n", desc_size); 
    Print(L"Descriptor Size: %d\n\n", sizeof(EFI_MEMORY_DESCRIPTOR)); 

    desc = buf; 
    while ((UINT8 *)desc <  (UINT8 *)buf + size) { 
        mapping_size =(UINTN) desc->NumberOfPages * PAGE_SIZE; 

        Print(L"[#%02d] Type: %s  Attr: 0x%x\n", i, memory_type_to_str(desc->Type), desc->Attribute); 
        Print(L"      Phys: %016llx-%016llx\n", desc->PhysicalStart, desc->PhysicalStart + mapping_size -1 ); 

        desc = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)desc + desc_size); 
        i++; 
    } 

    gBS->FreePool (buf); 
    return err; 
} 

EFI_STATUS 
EFIAPI
UefiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) 
{ 

    print_memory_map(); 

    return EFI_SUCCESS; 
}

 

运行结果如下:

step53

我们使用 GetMap >> result.txt 得到完整的输出结果如下:

Memory Map Size: 960
Map Key: 2841
Descriptor Version: 1
Descriptor Size: 48

Descriptor Size: 40

[#00] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000000CB0000-0000000000CCFFFF
[#01] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000000CD0000-000000000383FFFF
[#02] Type: EfiLoaderData Attr: 0xF
Phys: 0000000003840000-0000000003841FFF
[#03] Type: EfiLoaderCode Attr: 0xF
Phys: 0000000003842000-0000000003848FFF
[#04] Type: EfiLoaderData Attr: 0xF
Phys: 0000000003849000-000000000385EFFF
[#05] Type: EfiLoaderCode Attr: 0xF
Phys: 000000000385F000-00000000038ABFFF
[#06] Type: EfiConventionalMemory Attr: 0xF
Phys: 00000000038AC000-0000000003B09FFF
[#07] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B0A000-0000000003B56FFF
[#08] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000003B57000-0000000003B74FFF
[#09] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B75000-0000000003B75FFF
[#10] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000003B76000-0000000003B76FFF
[#11] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B77000-00000000048ABFFF
[#12] Type: EfiConventionalMemory Attr: 0xF
Phys: 00000000048AC000-0000000004990FFF
[#13] Type: EfiBootServicesCode Attr: 0xF
Phys: 0000000004991000-0000000004BABFFF
[#14] Type: EfiRuntimeServicesData Attr: 0xF
Phys: 0000000004BAC000-0000000004BEBFFF
[#15] Type: EfiRuntimeServicesCode Attr: 0xF
Phys: 0000000004BEC000-0000000004C2BFFF
[#16] Type: EfiReservedMemoryType Attr: 0xF
Phys: 0000000004C2C000-0000000004C2FFFF
[#17] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000004C30000-0000000004CAFFFF
[#18] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000004CB0000-0000000008CAFFFF
[#19] Type: EfiMemoryMappedIO Attr: 0x0
Phys: 0000000000BE0000-0000000000BEBFFF

和直接使用memmap取得的结果相同。需要注意的是:

1. 返回的Descriptor Size 并不是 sizeof(EFI_MEMORY_DESCRIPTOR)
2. 不知道EFI_VIRTUAL_ADDRESS VirtualStart; 是干什么用的

GetMap

参考:
1. http://blog.fpmurphy.com/2012/08/uefi-memory-v-e820-memory.html
可以在附件中找到该网页。它使用的是EFI_Toolkit 我没有编译过

Step to UEFI Shell(4)—-清屏功能

下面的代码展示了在Shell下如何编写一个清屏功能

EFI_SYSTEM_TABLE	*gST;

//
// Entry point function - HelloMain
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  gST = SystemTable;

  gST->ConOut->ClearScreen(gST->ConOut);
  gST->ConOut->EnableCursor(gST->ConOut,TRUE);

  return EFI_SUCCESS;
}

 

运行之后,会清屏并将光标至于屏幕左上角。

完整的代码 HelloWorld3

Step to UEFI Shell (3) —-获得命令行参数的方法

这里介绍如何获得Shell下的命令行参数。关键代码很简单,如下:

EFI_STATUS
EFIAPI
ShellAppMain (
	IN	UINTN 	Argc,
	IN 	CHAR16	**Argv
)
{
UINTN 	i;

	Print (L"Total %d args\n",Argc);
	for (i=0;i<Argc;i++)
 	{
	  Print (L"Arg[%d]= %s \n",i,*(Argv+i));
	}
	return EFI_SUCCESS;
}

 

uefistep3

完整的代码 HelloWorld2

获得当前硬盘上全部盘符

这一系列文章是根据cutebunny 的BLOG “windows的磁盘操作” 写成的,主要是部分修改原作中的代码,使之兼容Unicode和Windows 7 64bit. 原文可以在下面的网址找到

http://cutebunny.blog.51cto.com 。 本文是参考 “windows的磁盘操作之五——获取物理磁盘上的所有逻辑分区号”写成。

程序实现了获得当前硬盘上全部盘符的功能(分区可能没有分配盘符,比如:win7一般都会隐藏起来最前面的分区)。

// emuPart.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

/******************************************************************************
* Function: get disk's physical number from its drive letter
*           e.g. C-->0 (C: is on disk0)
* input: letter, drive letter
* output: N/A
* return: Succeed, disk number
*         Fail, -1
******************************************************************************/
DWORD GetPhysicalDriveFromPartitionLetter(TCHAR letter)
{
    HANDLE hDevice;               // handle to the drive to be examined
    BOOL result;                 // results flag
    DWORD readed;                   // discard results
    STORAGE_DEVICE_NUMBER number;   //use this to get disk numbers
 
    TCHAR path[MAX_PATH];
    wsprintf(path, L"\\\\.\\%c:", letter);
    hDevice = CreateFile(path, // drive to open
                         GENERIC_READ | GENERIC_WRITE,    // access to the drive
                         FILE_SHARE_READ | FILE_SHARE_WRITE,    //share mode
                         NULL,             // default security attributes
                         OPEN_EXISTING,    // disposition
                         0,                // file attributes
                         NULL);            // do not copy file attribute
    if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
    {
        fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
        return DWORD(-1);
    }
 
    result = DeviceIoControl(
                hDevice,                // handle to device
                IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode
                NULL,                            // lpInBuffer
                0,                               // nInBufferSize
                &number,           // output buffer
                sizeof(number),         // size of output buffer
                &readed,       // number of bytes returned
                NULL      // OVERLAPPED structure
            );
    if (!result) // fail
    {
        fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());
        (void)CloseHandle(hDevice);
        return (DWORD)-1;
    }
    printf("%d %d %d\n\n", number.DeviceType, number.DeviceNumber, number.PartitionNumber);
 
    (void)CloseHandle(hDevice);
    return number.DeviceNumber;
}

/******************************************************************************
* Function: get disk's drive letters from physical number
*           e.g. 0-->{C, D, E} (disk0 has 3 drives, C:, D: and E:)
* input: phyDriveNumber, disk's physical number
* output: letters, letters array
* return: Succeed, the amount of letters
*         Fail, -1
******************************************************************************/
DWORD GetPartitionLetterFromPhysicalDrive(DWORD phyDriveNumber, TCHAR **letters)
{
    DWORD mask;
    DWORD driveType;
    DWORD bmLetters;
    DWORD diskNumber;
    TCHAR path[MAX_PATH]; 
    TCHAR letter;
    DWORD letterNum;
    WORD i;
    TCHAR *p;
 
    bmLetters = GetLogicalDrives();
    if (0 == bmLetters)
    {
        return (DWORD)-1;
    }
 
    letterNum = 0;
    for (i = 0; i < sizeof(DWORD) * 8; i++)
    {
        mask = 0x1u << i;
        if ((mask & bmLetters) == 0)        //get one letter
        {
            continue;
        }
        letter = (TCHAR)(0x41 + i);    //ASCII change
        wsprintf(path, L"%c:\\", letter);
        driveType = GetDriveType(path);
        if (driveType != DRIVE_FIXED)
        {
            bmLetters &= ~mask;     //clear this bit
            continue;
        }
        diskNumber = GetPhysicalDriveFromPartitionLetter(letter);
        if (diskNumber != phyDriveNumber)
        {
            bmLetters &= ~mask;     //clear this bit
            continue;
        }
        letterNum++;
    }
 
    //build the result
    *letters = (TCHAR *)malloc(letterNum);
    if (NULL == *letters)
    {
        return (DWORD)-1;
    }
    p = *letters;
    for (i = 0; i < sizeof(DWORD) * 8; i++)
    {
        mask = 0x1u << i;
        if ((mask & bmLetters) == 0)
        {
            continue;
        }
        letter = (TCHAR)(0x41 + i);    //ASCII change
        *p = letter;
		printf("%c",letter);
        p++;
    }
   
    return letterNum;
}

int _tmain(int argc, _TCHAR* argv[])
{
	TCHAR	*List;

	GetPartitionLetterFromPhysicalDrive(0,&List);

	getchar();
	return 0;
}

 

运行结果如下 (注意,运行时需要管理员的权限)
emupart

最后一行给出了PhysicalDrive0 上的三个盘符

参考:
1.cutebunny 的BLOG “windows的磁盘操作” 可以在这里下载 WindowsDisk

使用 Arduino 打造一个硬件的Watchdog

我的台式机使用梅捷 SY-D2700-U3M 的主板,固化了Intel D2700 Atom CPU。具有零噪音(无风扇),节能环保,扩展丰富等等特点。唯一的问题是:经常硬盘丢失。具体是在使用中忽然BSOD,然后自动重启之后在Setup中无法找到硬盘,重启无效,必须关机一次再开机。从我的经验来看这绝非BIOS问题,并且实验确定不是硬盘问题。有人看守的情况下遇到这样的最多只是郁闷一些,但是作为下载机的时候,上述的问题就变得令人难以忍受。于是,考虑做一个硬件的 WatchDog,当出现问题的时候自动完成关闭开关,再次重启的动作。

BOM 如下:

1. Arduino UNO
2. WCH341 USB转串口
3. 面包板
4. 杜邦线+大头针
5. 继电器

说说上面这些东西,最重要的是继电器,就是下面这个东西。

1

这是两路的继电器,可以控制2组电路。性能指标是“交流电压最大250V,交流电流最大10A,直流电压最大30V,直流电流最大10A”。有4个输入针脚,分别是VCC GND,1IN 和2IN可以直连Arduino IO口。上电之后,1IN和2IN可以直接接入到VCC和GND上,应该能听到继电器“啪”的吸合的声音,此时测量受控端的电阻可以得知当前是断开还是链接。

2

再说一下喂狗的WCH341。 一般来说,Arduino的USB口可以用来取电和串口通讯。实际使用中我关闭台式机后,USB口供电也会随之切断,因此Arduino必须使用外部供电。既然使用了外部供电,串口也只能使用外部输入。刚开始选择的是FDTI 的USB转串口,一直无法正常通讯,最后测量发现它转出来的电压在 6.2v左右(TX/RX)远超过了.预期的5V。WCH341是之前玩单片机的剩下的,测量之后发现它的输出电压能够满足要求,于是就选用之。还有需要注意的是,完整的串口通讯实际上需要3跟线:TX RX和GND.

引出PowerButton的2个Pin.

3

继电器直接控制PowerButton Pin。

4

绿线接到继电器上进行控制

5

Arduino代码:

#include //引用库
/*
Simple LED sketch
*/

int led = 13; // Pin 13 当连接(相当于按下PowerButton)时点亮,断开时关闭
int cnt = 10; // Pin 10 控制继电器,高时断开,低时连接

unsigned long int elsp=0; //计算时间

void setup()
{
pinMode(led, OUTPUT); // Set pin 13 as digital out
pinMode(cnt, OUTPUT); // Set pin 10 as digital out
digitalWrite(led, LOW); // off
digitalWrite(cnt,HIGH); //Cut OFF

// Start up serial connection
Serial.begin(9600); //baud rate
Serial.flush();
elsp=millis();

wdt_enable(WDTO_8S); //这是Arduino的看门狗可以更换其他时间值
//注意,这里是8s,后面的延时不可以直接超过8s
}

void loop()
{

// Read any serial input
while (Serial.available() > 0)
{
if ((char) Serial.read() == ‘P’) {elsp=millis(); }
//当收到”P”时,重置计时器。换句话,我们用字符 “P” 来喂狗
}

if (abs(millis()-elsp)>300*1000UL) //300s = minutes
{
digitalWrite(led, HIGH); // on
//关机动作
digitalWrite(cnt,LOW); //继电器短路,相当于按下PowerButton
wdt_reset(); //重置Arduino内部看门狗
delay(6000); //这里相当于长按PowerButton 6s
wdt_reset(); //重置Arduino内部看门狗
digitalWrite(cnt,HIGH); //继电器断路,相当于松开PowerButton
//松开PowerButton 5s
delay(5000);
wdt_reset(); //重置Arduino内部看门狗
//开机上电
digitalWrite(cnt,LOW); //继电器短路,相当于按下PowerButton
delay(500); //按下500ms
digitalWrite(cnt,HIGH); //继电器断路,相当于松开PowerButton
elsp=millis();

digitalWrite(led, LOW); // off
wdt_reset(); //重置Arduino内部看门狗
}
delay(1000);
Serial.println(millis()-elsp);
wdt_reset(); //重置Arduino内部看门狗如果loop时间超过时限,系统会重启
}

6

除了Arduino的Firmware,PC端喂狗也需要一个程序。选择Delphi编写程序,实现两个功能:1.扫描COM Port找到进行通讯的端口 2.每隔一段时间就进行一次喂狗

(此处Delphi代码,Console模式)
HWDog

7

参考:

1. http://biosren.com/thread-6794-1-1.html 《改动代码提取器》

用 Arduino 打造PPT遥控器

前文提到,用Arduino模拟了USB键盘。做了一个自动锁屏幕装置外,又思考了一下用途还可以做个PPT的遥控器。为了实现遥控功能,需要搭配如下的元件。发射器和接收器是配对的。发射器上面有4个按钮,对应的接收器有D0-D3四个输出Pin.这个东西的名称叫做“2262/2272四路无线遥控套件M4非锁接收板 配四键无线遥控器”,价格不超过15元……

a

首先实验这个元件,直插面包板上,5V电是从USB口上取出来的。

b

用万用表验证,当按下A按钮时,D0会输出高电平。非锁的意思就是按一下就是一下(与之对应的是锁存,按一下后面会keep一段时间)。Enough,下面就可以继续其他。

简单的说,工作分为两步:第一步,制作Arduino模拟USB键盘;第二步,让这个模拟键盘发出左箭头和右箭头的键盘码。

使用的BOM如下:
A.USB公头(有供电和通讯能力的USB头皆可) x1
B.120欧电阻(原文建议68欧,但是我刚好没有。并联120欧充当60欧) x4
C. 2.2K欧电阻 x1
D.3.6伏稳压管 x2
E.遥控发射接收器(上面图片上的)

先说第一步,根据 《Arduino学习笔记A11 – Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备)》参考[1]。使用到BOM中提到的A-D,具体电路如下

c

按照上图设计,首先用面包板进行搭建,确定键盘能够正常工作。具体怎么做一个USB键盘这里就不说了。然后加入接收板。我使用大头针来作为引脚针,砍掉上面的大头,搭配杜邦线使用。用普通的排针总有太短没插到位的感觉,大头针长度足够,粗细也适合。

d

四个输出脚,只用了2个,D0和D3,间距大方便而已。如果有朋友喜欢还可以定义更多的功能。分别插在Arduino的D8和D10上。

f

程序如下:

最后实测,可以正常遥控,蓝色的USB线是下载程序用的,真正的USB是插在笔记本左边。

g

后面再考虑一下遥控的其他用途,比如:放炮点火什么的………多说一下,使用Arduino模拟USB之后,他上面的资源就非常非常少了,个人感觉能实现的功能还是挺有限的,做起来玩玩还可以,要想投入产品是够呛的。比如:因为速度的缘故,PC识别它模拟出来的USB键盘鼠标就要挺长时间。真打算做USB设备还是考虑传统的那些C51+D12之类的比较靠谱吧。

参考:
1. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=1137
2. http://www.lab-z.com/arduino-usb-keyboard-debug%E7%BB%8F%E9%AA%8C/
3. http://www.geek-workshop.com/thread-4006-1-1.html 利用Arduino上的atmega8u2制作红外遥控版PPT控制器

PuttyTel的辅助工具升级到 V2.1

最近升级了一下之前编写的一款方便串口调试的小工具 http://www.lab-z.com/puttytel%E7%9A%84%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7/

V2.0 可以自己设置波特率,也可以兼容更多的能接受命令行参数的串口软件。

V2.1 上一个版本会确认当前的命令行参数,对于普通用户来说是多余的。删掉这个重新编译。另外,调整代码将 JediAPILib.inc 放在源程序的目录下。

还有就是多显示一位版本号,之前只能显示大版本,比如:2 修改之后可以显示 2.1 这样的版本号。

screen

SerialPortChooserII2.1

SPCII,SPC2

用 Arduino 打造一个自动锁屏装置

上大学的时候,当团支书,负责同学交入Party申请书之类的事情。想入Party有一关是“群众评议”,就是看看是否有反对的意见。当时我和班级上的同学说“一个好人,进入Party是追求进步,是好事;一个坏人,进入Party会让群众队伍更纯洁,因此也是好事。大家都不要反对哈”。所以我们班级在这一关从来没有过什么问题。后来有一次,我去帮同学交入Party申请书,顺便讨价还价一下(我记得有规定是交了之后必须间隔一段才能进入正式的城西,为此我会和辅导员商量这个申请书的时间多多提前一些)。辅导员不在,我就稍等了一下。期间无聊,顺便翻翻其他班级的评议,结果让我大吃一惊,真有班级同学特别反对某个人的,罗列了很多条意见。正在我看在兴头上,辅导员看到急忙过来将东西收了起来,半开玩笑的批评另外帮忙的女同学,说这样的东西怎么能让这种人看到………所以信息安全非常重要。

当你起身去上厕所或者喝水的时候,是否可曾担心屏幕上的信息被人有意或者无意的看到?或者你外出办事,是否担心有人悄悄操作你的电脑?解决这个问题最简单的办法就是给Windows设置一个密码,然后在离开的时候按下 WinKey+L 。

下面就来使用Arduino制作一个自动完成这个“人走屏锁”的装置。

简单的说,工作分为两步:第一步,制作Arduino模拟USB键盘;第二步,让这个模拟键盘发出WinKey+L的键码。

使用的BOM如下:
A.USB公头(有供电和通讯能力的USB头皆可) x1
B.120欧电阻(原文建议68欧,但是我刚好没有所以并联2个来实现60欧) x4
C. 2.2K欧电阻 x1
D.3.6伏稳压管 x2
E.红外线传感器 x1 (用来实现人体感应)

先说第一步,根据 《Arduino学习笔记A11 – Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备)》参考[1]。使用到BOM中提到的A-D,具体电路如下

aa0

按照上图设计,首先用面包板进行搭建:

aa1

测试能够正常实现一个USB Keyboard的功能,具体调试可以参照《Arduino USB keyboard debug经验》。确定上述能够正常工作之后,进行简单的焊接,用大头针将Pin脚引出。

aa2

再使用纸壳做了一个盒子,将Arduino装了进去。
aa3

aa4

下一步的目标就是加入一个能够判断人体是否存在的功能了。最先想到的是人体感应模块。通常都是长得下面这个样子。

aa5

我也入手了一块,但是测试中感觉很奇怪,手放在它前面一段时间之后就没有输出了。后来再仔细阅读资料发现,这个东西应该叫做“人体运动感应模块”。是根据判断当前的红外线变化来判断是否有人体进入。如果想做到“人体感应”,还要加入更复杂的设计。
万幸,手边还有一块红外距离模块,这是用来判断一定距离内是否有遮挡的元件。探测距离可以在0-100cm以内调节。当有阻挡的时候输出低电平,反之输出高。

aa6

使用时,探头的VCC和GND同样取自USB供给。输出OUT连接到Arduino的D8。

aa7

程序如下:

#include "UsbKeyboard.h"
int KEYPIN = 8;                //使用D8作为检测输入,直接使用D1的话
//灌入电流过大,会导致死机。最好是加入限流电阻
//手边没有,就这样暂时这样了
unsigned long ElspTimer=0; 
void setup()
{
  TIMSK0 &= !(1 << TOIE0);     //这里的中断给USB使用了,所以Delay(), Millis()
//micros() delay() delayMicroseconds()统统不好用了
//取而代之的是用串口输出做的粗糙的延时
  pinMode(KEYPIN, INPUT);
  Serial.begin(9600);
}
void loop()
{
  UsbKeyboard.update();
  if(digitalRead(KEYPIN) == LOW)
  {
    ElspTimer=0;
  }
 ElspTimer++;
 Serial.println(ElspTimer);		//这里主要是为了延时使用
  if (ElspTimer>6000L)         //循环6000次,在Uno上是40s左右
    {
      UsbKeyboard.sendKeyStroke(KEY_L,MOD_GUI_LEFT); //发出WinKey+L 来锁定
      ElspTimer=0;
    }
}

 

这样,当人离开的时候,红外探头就无法检查到障碍物,会输出一个高电平。经过40s左右的延时,如果始终为高,就会模拟按下WinKey+L来锁定电脑。从而实现了人走屏锁的功能。
为了美观,最好再做个盒子之类的,我这里只是找了一个啤酒杯,露出探头和USB线。大功告成。

aa8

总结:Arduino通过IO来模拟一个低速设备能够完成一些有趣的功能,还有更多种的“玩法”等待我们去探索。

参考:
1. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=1137
2. http://www.lab-z.com/arduino-usb-keyboard-debug%E7%BB%8F%E9%AA%8C/