IO Port 的访问是对硬件的最基本操作,在 UEFI Shell 下我们通过 IoLib 中的IoRead8 IoRead16 IoRead32 这样的来进行读取。本文介绍Shell 下的 IoRead8 的具体实现。

首先编写一个代码用于测试:

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

extern  EFI_SYSTEM_TABLE    *gST;
extern  EFI_BOOT_SERVICES   *gBS;

/***
  Print a welcoming message.

  Establishes the main structure of the application.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
        Print(L"Read 80Port=%x\n",IoRead8(0x80));

    return(0);
}

编译之后,在 \Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\IOStudy\IOStudy\Makefile 可以看到使用了下面的 Library:

$(BIN_DIR)\MdePkg\Library\BaseIoLibIntrinsic\BaseIoLibIntrinsic\OUTPUT\BaseIoLibIntrinsic.lib

进一步研究,在 \MdePkg\Library\BaseIoLibIntrinsic\IoLibMsc.c 可以看到如下的实现:

/**
  Reads an 8-bit I/O port.

  Reads the 8-bit I/O port specified by Port. The 8-bit read value is returned.
  This function must guarantee that all I/O read and write operations are
  serialized.

  If 8-bit I/O port operations are not supported, then ASSERT().

  @param  Port  The I/O port to read.

  @return The value read.

**/
UINT8
EFIAPI
IoRead8 (
  IN      UINTN                     Port
  )
{
  UINT8                             Value;

  _ReadWriteBarrier ();
  Value = (UINT8)_inp ((UINT16)Port);
  _ReadWriteBarrier ();
  return Value;
}

为了证明这里就是我们要找到的具体实现,可以在  \MdePkg\Library\BaseIoLibIntrinsic\BaseIoLibIntrinsic.inf 加入如下代码:

 [BuildOptions]
  MSFT:*_*_X64_CC_FLAGS  = /FAsc /Od

重新编译(同时还需要删除 \Build\AppPkg\DEBUG_VS2015x86\X64\MdePkg\Library 中的BaseIoLibIntrinsic),之后可以在\Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\IOStudy\IOStudy 看到多出来一个IoLibMsc.cod 文件。

IoRead8	PROC						; COMDAT

; 73   : {

$LN3:
  00000	48 89 4c 24 08	 mov	 QWORD PTR [rsp+8], rcx
  00005	48 83 ec 18	 sub	 rsp, 24

; 74   :   UINT8                             Value;
; 75   : 
; 76   :   _ReadWriteBarrier ();
; 77   :   Value = (UINT8)_inp ((UINT16)Port);

  00009	0f b7 54 24 20	 movzx	 edx, WORD PTR Port$[rsp]
  0000e	ec		 in	 al, dx
  0000f	88 04 24	 mov	 BYTE PTR Value$[rsp], al

; 78   :   _ReadWriteBarrier ();
; 79   :   return Value;

  00012	8a 04 24	 mov	 al, BYTE PTR Value$[rsp]

; 80   : }

  00015	48 83 c4 18	 add	 rsp, 24
  00019	c3		 ret	 0
IoRead8	ENDP

在这里可以证明确实是IoLibMsc.c 中实现的。

_inp() 的作用是“从某个端口输入一个字节 (_inp)、一个字 (_inpw) 或一个双字 (_inpd)。” 但是看起来目前已经过时【参考1】。

与这个函数类似,还可以使用__inword 来实现读取某一个 Port,__outword直接写入某一个Port【参考2】。

参考:

  1. https://docs.microsoft.com/zh-cn/cpp/c-runtime-library/inp-inpw-inpd?view=vs-2019这些函数已过时。 从 Visual Studio 2015 开始,CRT 中不再提供这些函数。
  2. https://docs.microsoft.com/zh-cn/cpp/intrinsics/inword?view=vs-2017

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>