关于PCI_ROOT_BRIDGE_IO_PROTOCOL:
作用:该协议用于与PCI总线上的根桥(Root Bridge)进行通信,根桥是PCI总线的起始点,它负责转发数据和控制信号。
功能:EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL提供了访问PCI总线上的根桥所需的各种功能和方法,包括内存读写、I/O读写、配置空间读写等。
使用场景:主要用于与PCI总线上的根桥进行通信和配置,例如初始化PCI设备、读取和修改PCI设备的配置寄存器等。
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Protocol/PciRootBridgeIo.h>
EFI_STATUS EnumeratePciDevices()
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
// 获取PCI根桥IO协议的句柄
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiPciRootBridgeIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status))
{
// 错误处理
return Status;
}
UINTN Index;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo;
// 遍历所有PCI根桥IO协议
for (Index = 0; Index < HandleCount; Index++)
{
// 获取PCI根桥IO协议实例
Status = gBS->HandleProtocol(
HandleBuffer[Index],
&gEfiPciRootBridgeIoProtocolGuid,
(VOID **)&PciRootBridgeIo);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 获取PCI根桥上的所有PCI设备
UINT64 Segment;
Status = PciRootBridgeIo->Configuration->GetSegment(PciRootBridgeIo->Configuration, &Segment);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
UINT64 Bus;
for (Bus = 0; Bus <= 0xFF; Bus++)
{
UINT64 Device;
for (Device = 0; Device <= 0x1F; Device++)
{
UINT64 Function;
for (Function = 0; Function <= 0x7; Function++)
{
// 构建PCI设备地址
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS PciAddress;
PciAddress.Bus = (UINT32)Bus;
PciAddress.Device = (UINT32)Device;
PciAddress.Function = (UINT32)Function;
// 读取设备的Vendor ID和Device ID
UINT16 VendorId, DeviceId;
Status = PciRootBridgeIo->Pci.Read(
PciRootBridgeIo,
EfiPciWidthUint16,
PciAddress,
offsetof(PCI_DEVICE_HEADER, VendorId),
1,
&VendorId);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
Status = PciRootBridgeIo->Pci.Read(
PciRootBridgeIo,
EfiPciWidthUint16,
PciAddress,
offsetof(PCI_DEVICE_HEADER, DeviceId),
1,
&DeviceId);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打印设备的Vendor ID和Device ID
Print(L"PCI Device: Bus %02X, Device %02X, Function %X\n", Bus, Device, Function);
Print(L"Vendor ID: %04X\n", VendorId);
Print(L"Device ID: %04X\n\n", DeviceId);
}
}
}
}
// 释放句柄缓冲区
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
关于EFI_PCI_IO_PROTOCOL:
EFI_PCI_IO_PROTOCOL是用于与具体设备通信的协议,用于访问PCI总线上的具体设备。
在使用时,首先使用EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL来初始化和配置PCI总线上的根桥,然后使用EFI_PCI_IO_PROTOCOL来与具体设备进行通信和控制。
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL提供了用于访问配置空间的方法,可以用于获取和修改设备的配置信息。
EFI_PCI_IO_PROTOCOL提供了更多的操作方法,可以进行内存读写、I/O读写、数据传输等操作。
这里主要两个示例合二为一,读取配置空间以及BAR中的内容(网卡的mac地址):
#include <Uefi.h>
#include <Protocol/PciIo.h>
EFI_STATUS GetMacAddressFromPci()
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
// 获取所有PCI设备的句柄
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status))
{
// 错误处理
return Status;
}
UINTN Index;
EFI_PCI_IO_PROTOCOL *PciIo;
// 遍历所有PCI设备
for (Index = 0; Index < HandleCount; Index++)
{
// 获取PCI IO Protocol
Status = gBS->HandleProtocol(
HandleBuffer[Index],
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 判断是否为网卡设备(可根据设备的Vendor ID和Device ID进行判断)
UINT16 VendorId, DeviceId;
Status = PciIo->Pci.Read(
PciIo,
EfiPciIoWidthUint16,
0x00, // Vendor ID寄存器地址
1, // 寄存器数量
&VendorId);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
Status = PciIo->Pci.Read(
PciIo,
EfiPciIoWidthUint16,
0x02, // Device ID寄存器地址
1, // 寄存器数量
&DeviceId);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
if (VendorId == 0x10EC && DeviceId == 0x8168)
{
// 根据具体的Vendor ID和Device ID判断是否为目标网卡设备
// 读取MAC地址
UINT8 MacAddress[6];
Status = PciIo->Pci.Read(
PciIo,
EfiPciIoWidthUint8,
0x00, // MAC地址起始寄存器地址
6, // 寄存器数量
MacAddress);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打印MAC地址
Print(L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
MacAddress[0], MacAddress[1], MacAddress[2],
MacAddress[3], MacAddress[4], MacAddress[5]);
}
}
// 释放句柄缓冲区
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
获取BDF:
#include <Uefi.h>
#include <Protocol/PciIo.h>
EFI_STATUS GetPciBdf()
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
// 获取所有PCI设备的句柄
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiPciIoProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status))
{
// 错误处理
return Status;
}
UINTN Index;
EFI_PCI_IO_PROTOCOL *PciIo;
// 遍历所有PCI设备
for (Index = 0; Index < HandleCount; Index++)
{
// 获取PCI IO Protocol
Status = gBS->HandleProtocol(
HandleBuffer[Index],
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 获取BDF信息
UINTN SegmentNumber;
UINTN BusNumber;
UINTN DeviceNumber;
UINTN FunctionNumber;
Status = PciIo->GetLocation(
PciIo,
&SegmentNumber,
&BusNumber,
&DeviceNumber,
&FunctionNumber);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打印BDF信息
Print(L"BDF: %04x:%02x:%02x.%x\n",
SegmentNumber, BusNumber, DeviceNumber, FunctionNumber);
}
// 释放句柄缓冲区
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
当然还有另外一种读网卡mac地址的方式:
#include <Uefi.h>
#include <Protocol/SimpleNetwork.h>
EFI_STATUS GetMacAddress()
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
// 获取所有网络设备的句柄
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiSimpleNetworkProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status))
{
// 错误处理
return Status;
}
UINTN Index;
EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
// 遍历所有网络设备
for (Index = 0; Index < HandleCount; Index++)
{
// 获取Simple Network Protocol
Status = gBS->HandleProtocol(
HandleBuffer[Index],
&gEfiSimpleNetworkProtocolGuid,
(VOID **)&Snp);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
EFI_MAC_ADDRESS MacAddress;
// 获取MAC地址
Status = Snp->GetMacAddress(Snp, &MacAddress);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打印MAC地址
Print(L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
MacAddress.Addr[0], MacAddress.Addr[1], MacAddress.Addr[2],
MacAddress.Addr[3], MacAddress.Addr[4], MacAddress.Addr[5]);
}
// 释放句柄缓冲区
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
然后就是文件系统,比如读取USB设备中的文件:
#include <Uefi.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/LoadedImage.h>
EFI_STATUS ReadFileFromUSB()
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer;
UINTN HandleCount;
// 获取所有文件系统协议的句柄
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer);
if (EFI_ERROR(Status))
{
// 错误处理
return Status;
}
UINTN Index;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
EFI_FILE_PROTOCOL *RootDir;
// 遍历所有文件系统
for (Index = 0; Index < HandleCount; Index++)
{
// 获取文件系统协议
Status = gBS->HandleProtocol(
HandleBuffer[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&FileSystem);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打开根目录
Status = FileSystem->OpenVolume(FileSystem, &RootDir);
if (EFI_ERROR(Status))
{
// 错误处理
continue;
}
// 打开目标文件
EFI_FILE_PROTOCOL *FileHandle;
Status = RootDir->Open(
RootDir,
&FileHandle,
L"filename.txt", // 文件名
EFI_FILE_MODE_READ, // 打开模式
0); // 属性
if (EFI_ERROR(Status))
{
// 错误处理
RootDir->Close(RootDir);
continue;
}
// 读取文件内容
UINTN BufferSize = 1024; // 缓冲区大小
UINT8 Buffer[BufferSize];
Status = FileHandle->Read(
FileHandle,
&BufferSize,
Buffer);
if (EFI_ERROR(Status))
{
// 错误处理
FileHandle->Close(FileHandle);
RootDir->Close(RootDir);
continue;
}
// 在此处对读取的文件内容进行处理或打印
// 关闭文件和根目录
FileHandle->Close(FileHandle);
RootDir->Close(RootDir);
}
// 释放句柄缓冲区
gBS->FreePool(HandleBuffer);
return EFI_SUCCESS;
}
再一个就是USB设备扫描:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/UsbIo.h>
#include <Protocol/Usb2HostController.h>
EFI_STATUS ScanUsbDevices()
{
EFI_STATUS status;
EFI_HANDLE *controllerHandles;
UINTN controllerHandleCount;
UINTN index;
// 获取所有USB控制器的句柄
status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiUsb2HcProtocolGuid,
NULL,
&controllerHandleCount,
&controllerHandles
);
if (EFI_ERROR(status)) {
Print(L"Failed to locate USB controllers. Error: %r\n", status);
return status;
}
// 遍历所有USB控制器并扫描设备
for (index = 0; index < controllerHandleCount; index++) {
EFI_USB2_HC_PROTOCOL *usbHc;
status = gBS->OpenProtocol(
controllerHandles[index],
&gEfiUsb2HcProtocolGuid,
(VOID **)&usbHc,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(status)) {
Print(L"Failed to open USB2_HC_PROTOCOL. Error: %r\n", status);
continue;
}
// 扫描USB设备
EFI_USB_DEVICE_DESCRIPTOR deviceDescriptor;
EFI_USB_INTERFACE_DESCRIPTOR interfaceDescriptor;
EFI_HANDLE deviceHandle = NULL;
for (UINT8 port = 1; port <= usbHc->GetRootHubPortNumber(usbHc); port++) {
status = usbHc->GetRootHubPortStatus(usbHc, port, &portStatus);
if (EFI_ERROR(status)) {
// 处理错误
continue;
}
if (portStatus.PortStatus.PortConnected) {
// 获取USB设备描述符
status = usbHc->GetRootHubPortStatus(usbHc, port, &deviceDescriptor);
if (EFI_ERROR(status)) {
// 处理错误
continue;
}
// 创建USB设备的句柄
status = usbHc->CreateDevice(usbHc, &deviceHandle);
if (EFI_ERROR(status)) {
// 处理错误
continue;
}
// 设置USB设备的配置
status = usbHc->SetConfig(usbHc, deviceHandle, deviceConfig);
if (EFI_ERROR(status)) {
// 处理错误
continue;
}
// 获取USB设备的接口描述符
status = usbHc->GetInterfaceDescriptor(usbHc, deviceHandle, &interfaceDescriptor);
if (EFI_ERROR(status)) {
// 处理错误
continue;
}
// 处理扫描到的USB设备
// 关闭USB设备的句柄
usbHc->DestroyDevice(usbHc, deviceHandle);
deviceHandle = NULL;
}
}
上述代码可以说是一个模版,需要用到的时候直接参照就行,当然还有比如PCI设备的Capability部分怎么去获取等等
获取DEVICE PATH:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DevicePathLib.h>
EFI_STATUS PrintDevicePath(EFI_HANDLE deviceHandle)
{
EFI_STATUS status;
EFI_DEVICE_PATH_PROTOCOL *devicePath;
// 获取设备的Device Path
status = gBS->HandleProtocol(
deviceHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&devicePath
);
if (EFI_ERROR(status)) {
Print(L"Failed to get Device Path Protocol. Error: %r\n", status);
return status;
}
// 打印设备的Device Path
CHAR16 *devicePathString = ConvertDevicePathToText(devicePath, FALSE, FALSE);
if (devicePathString != NULL) {
Print(L"Device Path: %s\n", devicePathString);
FreePool(devicePathString);
}
return EFI_SUCCESS;
}
EFI_STATUS EFIAPI UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS status;
EFI_HANDLE *deviceHandles;
UINTN deviceHandleCount;
UINTN index;
// 获取所有设备的句柄
status = gBS->LocateHandleBuffer(
AllHandles,
NULL,
NULL,
&deviceHandleCount,
&deviceHandles
);
if (EFI_ERROR(status)) {
Print(L"Failed to locate devices. Error: %r\n", status);
return status;
}
// 遍历所有设备并打印Device Path
for (index = 0; index < deviceHandleCount; index++) {
status = PrintDevicePath(deviceHandles[index]);
if (EFI_ERROR(status)) {
// 处理错误
}
}
// 释放内存
FreePool(deviceHandles);
return EFI_SUCCESS;
}