UEFI学习(五)-PCIE学习与递归实现遍历PCIe设备

UEFI学习(五)——PCIE学习


基本概念

  1. 编码技术:在高速的串行数据传输中,传送的数据被编码成自同步的数据流,就是将数据和时钟组合成单一的信号进行传送,使得接收方能容易准确地将数据和时钟分离,而且要达到令人满意的误码率,其关键技术在于串行传输中数据的编码方法。

  2. 直流均衡: 确保“0” 码元与“1” 码元个数的一致。

  3. 8B/10B 码的优势:(1)采用8B/10B编码方法,数据流中连续的“1” 或连续的“0”不超过5个,使接收端锁相环( PLL)能正常工作,避免接收端时钟漂移或同步丢失而引起数据丢失。 保证了1和0的相对平衡组合,而与数据值无关,简化了时钟恢复,降低了接收机成本。(2)DC补偿(3)检错:8B/10B编码采用冗余方式,将8位的数据和一些特殊字符按照特定的规则编码成10位的数据,根据这些规则,能检测出传输过程中单个和多个比特误码

  4. 每一个PCIe Function都有一个唯一的 PFA(PCI Function Address) 。 PFA由 bus number、device number、function number组成。在PFA中,Bus Number占用8位,Device Number占用5位,Function Number占用3位。显然,PCIe总线最多支持256个子总线,每个子总线最多支持32个设备,每个设备最多支持8个功能。

  5. PCIe设备的连接关系:在这里插入图片描述

  6. PCIe Device的地址映射:在这里插入图片描述

  7. PCIe配置空间:PCIe spec规定PCIe设备必须提供独立的配置空间。配置空间里面存储了一些基本信息,如生产商、设备ID、设备类型、IRQ中断号,还有内部IO空间和MMIO空间的起始地址和大小。
    每个Function(功能)有自己的4K配置空间。PCIe配置空间由两部分组成:PCI兼容配置空间(256Byte) 和 PCIe扩展配置空间(4K-256Bytes)。配置空间的定义分为两个Type, Type0和Type1。Type0 定义Device, Type1定义Bridge。

  8. Type 0 Endpoint(终端设备)的配置:在这里插入图片描述

  9. Type 1 Bridge(桥接器)的配置:在这里插入图片描述

参考文章: https://blog.csdn.net/yingqiangli/article/details/109161667?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163953316716780271912425%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163953316716780271912425&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-3-109161667.pc_search_result_control_group&utm_term=PCIE&spm=1018.2226.3001.4187

递归实现PCIe设备遍历

实现功能:

  1. 串口输出函数编写
  2. 仿照printf函数编写自己的串口格式化输出函数,即传入参数个数不定
  3. 递归实现PCIe设备遍历并通过串口将信息输出(做完才发现自己其实也是基于深度优先的)

要点:

(1)串口配置:个人采用的是NCT5585D,在datasheet的第10章有描述了UART PORT的相关内容,如果要调用串口则先需要将全局变量寄存器中的相关使能位打开,而根据datesheet得知串口的配置寄存器位于IO space中,其基地址存放在logical device 2的CR 60h,61h中。而串口的配置寄存器就是基于此基地址加上偏移得到从下图的串口配置总表可以看到某些寄存器对于基地址有相同的偏移,其实是描述读和写两个过程的对应作用,根据描述可知,通过设置了UART Control Register (UCR) (Read/Write)的最高位BDLAB为0后,便能通过对偏移为0的寄存器进行串口读写操作。在这里插入图片描述
在这里插入图片描述

(2)仿写printf函数:


void myDebugWriteStr(UINT8 *str,...)
{
	UINT8 x = 0;
	UINTN receviexfrommat=0;
	UINT8 receiveBuffer[20];
	char *pstr = NULL;
	va_list arg;     //定义va_list类型变量arg
	va_start(arg,str); //arg初始化,将arg指向第一个可选参数
	assert(str);     //保证str不为空
	while (*str)
	{
		if(*str == '%')
		{
			str++;
			switch(*str)
			{
				case 's':
					{
						 pstr = va_arg(arg,char*);  //va_arg的第二个参数是你要返
						                            //回的参数的类型,此处是char *
						 printf("%s\n", pstr);
						 while(*pstr!='\n')
						 {
							 myDebugWrite(*pstr);
							 gBS->Stall(1000);
							 pstr++;
						 }
						 break;
					}
				case 'c':
					{
						x = va_arg(arg,char); //返回参数列表中的当前参数,并使a
						                     //rg指向参数列表中的下一个参数
						printf("%c\n",x);
						myDebugWrite((UINT8)x);
						gBS->Stall(1000);
						x++;
						break;
					}
				case 'x':
					{
						receviexfrommat=va_arg(arg,UINTN);
						printf("%08x\n",receviexfrommat );
						sprintf(receiveBuffer,"%08x",receviexfrommat);
						printf("%s\n",receiveBuffer);
						for(int i=0;i<8;i++)
						{
							myDebugWrite((UINT8)receiveBuffer[i]);
							gBS->Stall(1000);
						}
						printf("\n");
						break;
					}
				case '0':
					{
						printf("testpoint:%c %c\n", *(str+1),*(str+2));
						if(*(str+1)=='2'&&(*(str+2)=='x'||*(str+2)=='X'))
						{
							receviexfrommat=va_arg(arg,UINTN);
							//printf("%02x\n",receviexfrommat );
							sprintf(receiveBuffer,"%02x",receviexfrommat);
							//printf("%s\n",receiveBuffer);
							for(int i=0;i<2;i++)
							{
								myDebugWrite((UINT8)receiveBuffer[i]);
								gBS->Stall(1000);
							}
							str++;
							str++;
							break;
						}
						else if(*(str+1)=='4'&&(*(str+2)=='x'||*(str+2)=='X'))
						{
							receviexfrommat=va_arg(arg,UINTN);
							//printf("%02x\n",receviexfrommat );
							sprintf(receiveBuffer,"%04x",receviexfrommat);
							//printf("%s\n",receiveBuffer);
							for(int i=0;i<4;i++)
							{
								myDebugWrite((UINT8)receiveBuffer[i]);
								gBS->Stall(1000);
							}
							str++;
							str++;
							break;							
						}
						else if(*(str+1)=='8'&&(*(str+2)=='x'||*(str+2)=='X'))
						{
							receviexfrommat=va_arg(arg,UINTN);
							//printf("%02x\n",receviexfrommat );
							sprintf(receiveBuffer,"%08x",receviexfrommat);
							//printf("%s\n",receiveBuffer);
							for(int i=0;i<8;i++)
							{
								myDebugWrite((UINT8)receiveBuffer[i]);
								gBS->Stall(1000);
							}
							str++;
							str++;
							break;							
						}
						else
						{
							myDebugWrite(*(str-1));
							gBS->Stall(1000);
							myDebugWrite(*str);
							gBS->Stall(1000);
							break;
						}

					}
				default:
					{
						myDebugWrite(*(str-1));
						gBS->Stall(1000);
						myDebugWrite(*str);
						gBS->Stall(1000);
						break;
					}	
			}	
			str++;
		}
		else
		{
			myDebugWrite(*str);
			gBS->Stall(1000);
			str++;
		}
	}
	va_end(arg);   //把arg指针清为NULL
}

(3)递归实现PCIe设备遍历:

1. 获取分配给PCIe设备的内存空间的基地址:通过调用gEfiPciRootBridgeIoProtocolGuid,根据
gPCIRootBridgeIO->Pci.Read (
                              gPCIRootBridgeIO,
                              EfiPciWidthUint8,
                              Address,
                              1,
                              &regReceive
                              );
来读取指定地址:Address,数据长度:1,返回接受到的数据到指定buffer:&regReceive,其中Address = EFI_PCI_ADDRESS (Bus, Device, Func, Offset);
其中EFI_PCI_ADDRESS (Bus, Device, Func, Offset)
为#define EFI_PCI_ADDRESS(bus, dev, func, reg) \
((UINT64) ((((UINTN) bus) << 24) + (((UINTN) dev) << 16) + (((UINTN) func) << 8) + ((UINTN) reg)))
通过此功能将其封装成读取指定BDF(Bus, Device, Func, Offset)的功能函数

2. 递归实现的思想:参考文章链接: [https://www.cnblogs.com/szhb-5251/p/11620310.html](https://www.cnblogs.com/szhb-5251/p/11620310.html)
在实现遍历的时候遇到的难点是:设备号(device)与其功能号(func)因为由硬件分配并不连续所以采用枚举的方式去遍历寻找;
而面对switch桥时,也要考虑桥是否有其他功能号,桥的其他功能号也可能作为switch桥或者设备,但此时若为设备,则旗下没有第二功能,也无需进行功能号的循环遍历寻找;

完整PCIe设备遍历实现工程代码【点击此处超链接】

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: UEFI BIOS Utility-EZ Mode是一种简单易用的BIOS设置界面,它提供了基本的系统信息、启动选项、调整CPU和内存频率、设置风扇控制等功能。它的设计使得用户可以轻松地进行基本的BIOS设置,而不需要深入了解BIOS的复杂性。 ### 回答2: UEFI BIOS Utility - EZ Mode是一种UEFI BIOS配置工具,它旨在使用户可以轻松管理和调整基本系统设置。EZ Mode提供了一个简化的图形用户界面面板,使用户能够快速而准确地调整各种BIOS选项。 EZ Mode界面以图形化方式呈现各种设置和参数,这使得用户可以更轻松地了解它们的含义和作用。例如,在EZ Mode,用户可以快速地查看CPU速度、内存用量、系统温度、风扇转速等参数,并进行必要的调整以改善系统的性能和稳定性。 除了常规的设置,EZ Mode还提供了一些高级功能。例如,用户可以选择开启或关闭诸如Intel快速启动技术、USB 3.0支持、SATA控制器等功能,以最大程度地优化系统性能。 此外,EZ Mode还支持自动检测并更新BIOS固件,这可以保证系统始终运行在最新的稳定版本。 总之,UEFI BIOS Utility - EZ Mode 是一个非常有用和功能强大的BIOS配置工具,它使用户在处理系统设置和参数时更加轻松。无论是普通用户还是高级用户,都可以从EZ Mode的功能受益。 ### 回答3: UEFI BIOS Utility是现代计算机使用的一种新型Firmware。UEFI位于操作系统和硬件之间,主要负责计算机启动过程的硬件自检以及操作系统的加载过程。UEFI BIOS Utility的EZ模式是一种简化操作的模式,旨在提供用户一个更加直观的界面,以便用户轻松地对计算机进行配置。 UEFI BIOS Utility的EZ模式提供了一个清晰的界面,可以让用户更加直观地了解计算机的硬件配置,系统信息以及启动选项等。在这种模式下,用户可以很方便地进行一些常见的设置,例如修改系统时间,设置启动顺序等等。同时,EZ模式还提供了导航面板,让用户轻松地找到需要的选项。 一些重要的系统配置也可以在EZ模式下进行调整。例如,用户可以配置CPU的频率和电压,设置内存检测参数等等。此外,用户还可以对硬盘进行分区,修改RAID设置和SATA模式等。 总之,UEFI BIOS Utility的EZ模式提供了一个真正的易用界面,让用户轻松处理大量的系统配置和硬件设置。当您需要进行系统调整的时候,把您的注意力放在EZ模式,就不需要担心在BIOS迷路了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值