【UEFI基础】BIOS下的NVMe

什么是NVMe

NVMe全称NonVolatile Memory Express(非易失性内存主机控制器接口规范),其官方(NVMe官网NVM Express)定义将其描述为“一个开放的标准和信息集合,以充分释放非易失性存储在从移动端到数据中心的所有类型的计算环境中能够提供的优势。NVMe从底层开始设计,为当前和未来的NVM技术提供高带宽和低延迟存储访问。”

NVMe的历史

机械硬盘(HDD,Hard Disk Drive的缩写形式)其内部主要由马达(控制电机)、磁盘片、磁头、转轴、磁头控制器、数据转换器、接口和缓存等几个部分组成,它存在体积大、使用过程容易受震动和碰撞等影响导致损坏、速度慢等问题。固态硬盘(SSD,Solid State Drives的缩写形式)由主控和固态存储芯片(通常是NAND Flash)阵列构成,如下图所示:

在这里插入图片描述

相比与机械硬盘,其存在的优点包括:读写速度快、防震抗摔性、低功耗、无噪音、工作温度范围大、轻便等等。

SSD开始作为存储使用时,一般使用如SATA、SAS或光纤通道等接口与计算机的总线连接。随着固态硬盘在大众市场上的流行,SATA已成为个人计算机中连接SSD的最典型方式。但是,SATA的设计主要是作为机械硬盘的接口,并随着时间的推移越来越难满足速度日益提高的SSD。随着在大众市场的流行,许多固态硬盘的数据速率提升已经放缓。不同于机械硬盘,部分SSD已受到SATA最大吞吐量的限制。

在NVMe出现之前,高端SSD只得以采用PCI Express总线制造,但需使用非标准规范的接口。若使用标准化的SSD接口,操作系统只需要一个驱动程序就能使用匹配规范的所有SSD。这也意味着每个SSD制造商不必用额外的资源来设计特定接口的驱动程序。

2009年Intel开始着手寻找SATA的替代方案。SATA作为串行接口,采用AHCI规范,其已经成为制约SSD速度的瓶颈。AHCI只有1个命令队列,队列深度32;而NVMe可以有65535个命令队列,每个队列都可以深达65536个命令。NVMe也充分使用了MSI的2048个中断向量优势,延迟大大减小。

后续的版本:

在这里插入图片描述

NVMe基础

NVMe是一种用于高速并行数据传输的协议,在存储架构中的位置:

在这里插入图片描述

使用NVMe可减少固态硬中使用的每个输入/输出(I/O)的系统开销。由于设备驱动器的更改允许并行和轮询,因此NVMe SSD能够提供比传统硬盘驱动器(HDD)更快的响应时间。这些改进有助于减少延迟,使其成为企业工作负载以及众多消费者和专业应用程序的理想选择。

NVMe基于PCIe,所以它不像SATA这样需要额外的控制器,而是可以直接与CPU或者芯片组连接:

在这里插入图片描述

以下是使用 NVMe 存储相对于 SAS 或 SATA 驱动器的一些优势:

  • 提升性能:NVMe技术可以使用PCIe将SSD存储直接连接到服务器或中央处理单元(CPU)。凭借性能的显著提高,对于游戏玩家、视频编辑器和其他需要比SAS或SATA HDD更高性能的用户来说,NVMe技术成为了首选数据存储/传输选项。
  • 速度更快:NVMe驱动器可以提供比 SAS或 SATA 驱动器更高的速度,因为它们可以更快地发送和接收NVMe命令并提供更好的吞吐量。
  • 提高兼容性:NVMe被广泛视为比SAS/SATA更具兼容性的选项,并且随着AI、ML和云计算等快速发展的关键技术的发展而经常更新。NVMe技术可以与所有现代操作系统无缝协作,包括手机、笔记本电脑和游戏机。
  • 改进的带宽:PCIe连接比SAS或SATA端口更宽并且具有更多的带宽。它还随着每一代的改进而改进,带宽是上一代的两倍。SAS和SATA的带宽连接要低得多并且是固定的,因此它们不会随着时间的推移而改进。PCIe连接脱颖而出的另一个特点是它们在“通道”中可扩展,因此即使在同一代产品中,用户也可以通过两倍的通道数量将带宽加倍。

BIOS下的NVMe驱动

BIOS下的NVMe驱动对应模块时edk2\MdeModulePkg\Bus\Pci\NvmExpressDxe\NvmExpressDxe.inf。它时一个UEFI Model Driver,所以有以下的接口:

//
// NVM Express Driver Binding Protocol Instance
//
EFI_DRIVER_BINDING_PROTOCOL  gNvmExpressDriverBinding = {
  NvmExpressDriverBindingSupported,
  NvmExpressDriverBindingStart,
  NvmExpressDriverBindingStop,
  0x10,
  NULL,
  NULL
};

该模块依赖于gEfiDevicePathProtocolGuidgEfiPciIoProtocolGuid,最终提供的主要接口是gEfiNvmExpressPassThruProtocolGuidgEfiBlockIoProtocolGuidgEfiBlockIo2ProtocolGuidgEfiDiskInfoProtocolGuid

IN
IN
OUT
OUT
OUT
OUT
gEfiDevicePathProtocolGuid
NVMe
gEfiPciIoProtocolGuid
gEfiNvmExpressPassThruProtocolGuid
gEfiBlockIoProtocolGuid
gEfiBlockIo2ProtocolGuid
gEfiDiskInfoProtocolGuid

NvmExpressDriverBindingSupported

Supported主要就是判断Device Path和PCI IO的情况。

前者需要判断Device Path的类型,主要需要关注的是RemainingDevicePath部分:

  //
  // Check whether device path is valid
  //
  if (RemainingDevicePath != NULL) {
    //
    // Check if RemainingDevicePath is the End of Device Path Node,
    // if yes, go on checking other conditions
    //
    if (!IsDevicePathEnd (RemainingDevicePath)) {
      //
      // If RemainingDevicePath isn't the End of Device Path Node,
      // check its validation
      //
      DevicePathNode.DevPath = RemainingDevicePath;

      if ((DevicePathNode.DevPath->Type    != MESSAGING_DEVICE_PATH) ||
          (DevicePathNode.DevPath->SubType != MSG_NVME_NAMESPACE_DP) ||
          (DevicePathNodeLength (DevicePathNode.DevPath) != sizeof (NVME_NAMESPACE_DEVICE_PATH)))
      {
        return EFI_UNSUPPORTED;
      }
    }
  }

这是因为NVMe存在逻辑上的分区,称为Namespace。

后者主要是通过PCI IO获取Class Code:

  //
  // Examine Nvm Express controller PCI Configuration table fields
  //
  if ((ClassCode[0] != PCI_IF_NVMHCI) || (ClassCode[1] != PCI_CLASS_MASS_STORAGE_NVM) || (ClassCode[2] != PCI_CLASS_MASS_STORAGE)) {
    Status = EFI_UNSUPPORTED;
  }

两者判断都成功之后才会执行Start函数。

NvmExpressDriverBindingStart

该函数中的操作包括:

  • 初始化NVME_CONTROLLER_PRIVATE_DATA
//
// Nvme private data structure.
//
struct _NVME_CONTROLLER_PRIVATE_DATA {
  UINT32                                Signature;

  EFI_HANDLE                            ControllerHandle;
  EFI_HANDLE                            ImageHandle;
  EFI_HANDLE                            DriverBindingHandle;

  EFI_PCI_IO_PROTOCOL                   *PciIo;
  UINT64                                PciAttributes;

  EFI_DEVICE_PATH_PROTOCOL              *ParentDevicePath;

  EFI_NVM_EXPRESS_PASS_THRU_MODE        PassThruMode;
  EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL    Passthru;

  //
  // pointer to identify controller data
  //
  NVME_ADMIN_CONTROLLER_DATA            *ControllerData;

  //
  // 6 x 4kB aligned buffers will be carved out of this buffer.
  // 1st 4kB boundary is the start of the admin submission queue.
  // 2nd 4kB boundary is the start of the admin completion queue.
  // 3rd 4kB boundary is the start of I/O submission queue #1.
  // 4th 4kB boundary is the start of I/O completion queue #1.
  // 5th 4kB boundary is the start of I/O submission queue #2.
  // 6th 4kB boundary is the start of I/O completion queue #2.
  //
  UINT8          *Buffer;
  UINT8          *BufferPciAddr;

  //
  // Pointers to 4kB aligned submission & completion queues.
  //
  NVME_SQ        *SqBuffer[NVME_MAX_QUEUES];
  NVME_CQ        *CqBuffer[NVME_MAX_QUEUES];
  NVME_SQ        *SqBufferPciAddr[NVME_MAX_QUEUES];
  NVME_CQ        *CqBufferPciAddr[NVME_MAX_QUEUES];

  //
  // Submission and completion queue indices.
  //
  NVME_SQTDBL    SqTdbl[NVME_MAX_QUEUES];
  NVME_CQHDBL    CqHdbl[NVME_MAX_QUEUES];
  UINT16         AsyncSqHead;

  //
  // Flag to indicate internal IO queue creation.
  //
  BOOLEAN        CreateIoQueue;

  UINT8          Pt[NVME_MAX_QUEUES];
  UINT16         Cid[NVME_MAX_QUEUES];

  //
  // Nvme controller capabilities
  //
  NVME_CAP       Cap;

  VOID           *Mapping;

  //
  // For Non-blocking operations.
  //
  EFI_EVENT      TimerEvent;
  LIST_ENTRY     AsyncPassThruQueue;
  LIST_ENTRY     UnsubmittedSubtasks;
};
  • 创建与NVMe交互的Buffer。
    //
    // 6 x 4kB aligned buffers will be carved out of this buffer.
    // 1st 4kB boundary is the start of the admin submission queue.
    // 2nd 4kB boundary is the start of the admin completion queue.
    // 3rd 4kB boundary is the start of I/O submission queue #1.
    // 4th 4kB boundary is the start of I/O completion queue #1.
    // 5th 4kB boundary is the start of I/O submission queue #2.
    // 6th 4kB boundary is the start of I/O completion queue #2.
    //
    // Allocate 6 pages of memory, then map it for bus master read and write.
    //
    Status = PciIo->AllocateBuffer (
                      PciIo,
                      AllocateAnyPages,
                      EfiBootServicesData,
                      6,
                      (VOID **)&Private->Buffer,
                      0
                      );
    if (EFI_ERROR (Status)) {
      goto Exit;
    }

    Bytes  = EFI_PAGES_TO_SIZE (6);
    Status = PciIo->Map (
                      PciIo,
                      EfiPciIoOperationBusMasterCommonBuffer,
                      Private->Buffer,
                      &Bytes,
                      &MappedAddr,
                      &Private->Mapping
                      );

    if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (6))) {
      goto Exit;
    }
  • 构建EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL,它是与NVMe交互的接口。
//
// Protocol Interface Structure
//
struct _EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL {
  EFI_NVM_EXPRESS_PASS_THRU_MODE                  *Mode;
  EFI_NVM_EXPRESS_PASS_THRU_PASSTHRU              PassThru;
  EFI_NVM_EXPRESS_PASS_THRU_GET_NEXT_NAMESPACE    GetNextNamespace;
  EFI_NVM_EXPRESS_PASS_THRU_BUILD_DEVICE_PATH     BuildDevicePath;
  EFI_NVM_EXPRESS_PASS_THRU_GET_NAMESPACE         GetNamespace;
};
  • 创建定时器处理Asynchronous I/O。
    //
    // Start the asynchronous I/O completion monitor
    //
    Status = gBS->CreateEvent (
                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
                    TPL_NOTIFY,
                    ProcessAsyncTaskList,
                    Private,
                    &Private->TimerEvent
                    );
    if (EFI_ERROR (Status)) {
      goto Exit;
    }

    Status = gBS->SetTimer (
                    Private->TimerEvent,
                    TimerPeriodic,
                    NVME_HC_ASYNC_TIMER
                    );
    if (EFI_ERROR (Status)) {
      goto Exit;
    }
  • 遍历NVMe所有的Namespace,并在其上安装Block IO等Protocol,重点函数是:
/**
  Check if the specified Nvm Express device namespace is active, and create child handles
  for them with BlockIo and DiskInfo protocol instances.

  @param[in] Private         The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.
  @param[in] NamespaceId     The NVM Express namespace ID  for which a device path node is to be
                             allocated and built. Caller must set the NamespaceId to zero if the
                             device path node will contain a valid UUID.

  @retval EFI_SUCCESS        All the namespaces in the device are successfully enumerated.
  @return Others             Some error occurs when enumerating the namespaces.

**/
EFI_STATUS
EnumerateNvmeDevNamespace (
  IN NVME_CONTROLLER_PRIVATE_DATA  *Private,
  UINT32                           NamespaceId
  )

有了Block IO等接口,就可以接入到了UEFI BIOS中,后续可以进行读写NVMe和创建启动项等操作。

参考资料

  1. 了解 SSD 技术:NVMe、SATA、M.2 - 金士顿科技 (kingston.com.cn)
  2. 什么是 NVMe?| IBM
BIOS 注入 NVMe 驱动是指在计算机的 BIOS 中添加对 NVMe(非易失性内存快速存储)驱动的支持。NVMe 是一种高性能、低延迟的存储接口协议,可以提供更快的数据访问速度和更高的存储性能。 首先,为了将 NVMe 驱动注入到 BIOS 中,我们需要获取 NVMe 驱动程序的适当版本,并确保其与计算机的硬件和操作系统兼容。然后,我们需要使用适用于 BIOS 编程的工具,如编程器或 UEFI BIOS 更新工具。 在注入NVMe驱动之前,请务必备份计算机的当前 BIOS 设置。然后,打开使用 BIOS 编程工具的软件,并选择需要编辑的 BIOS 文件。在选择适当的文件后,我们需要浏览并找到与驱动程序相关的部分。这通常位于存储设备选项或高级选项菜单中。 接下来,我们将需要将 NVMe 驱动程序文件加载到工具中,并按照工具的指导完成注入过程。通常,这涉及到选择“添加驱动程序”或类似的选项,并浏览并选择所需的驱动程序文件。然后,我们需要跟随工具的指导添加驱动程序,并在完成后保存所做的更改。 完成此过程后,我们可以运行让计算机重启以应用修改的工具。重新启动计算机后,我们可以进入 BIOS 设置界面,并确认 NVMe 驱动程序是否成功注入。在计算机启动时,它应该能够检测到并支持 NVMe 存储设备。 通过将 NVMe 驱动程序注入到 BIOS 中,我们可以使计算机更好地支持 NVMe 存储设备,并获得更快的存储性能。这对于那些使用 NVMe 存储设备的用户来说特别重要,因为它们可以显着提高系统的响应速度和数据传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值