UEFI开发——编写一个简单的PPI

2024-08-28 22:04
文章标签 简单 开发 编写 uefi ppi

本文主要是介绍UEFI开发——编写一个简单的PPI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PEI阶段是由一个个的PEIM实现的,而PPI是PEIM之间互相调用的接口。简单来说,PPI只是一个接口,接口里面有成员函数,想调用某个函数就必须通过该接口。

PPI通过PPI描述符来描述其特性,PPI描述符是一个数据结构,定义如下:

typedef struct {//UINTN是一个无符号的整数,Flags是一系列诸如EFI_PEI_PPI_DESCRIPTOR_***的组合UINTN       Flags;//接口的名字,即它的GUIDEFI_GUID    *Guid;//指向PPI的指针,包含安装服务的信息VOID        *Ppi;
} EFI_PEI_PPI_DESCRIPTOR;

在利用PPI的过程中,有个几个重要的PPI服务:

InstallPpi():安装PPI到PEI foundation

LocatePpi():根据PPI名字GUID从PEI foundation中找到Interface

NotifyPpi():PPI里的function不会再派发时就执行,会有一个判定条件,通知系统这个PPI会在某个PPI被安装时才执行。

1、编写一个InstallPPI的源文件:

该模块通过PeiServicesInstallPpi注册接口,以提供服务

/**This service enables a given PEIM to register an interface into the PEI Foundation.@param  PpiList               A pointer to the list of interfaces that the caller shall install.@retval EFI_SUCCESS           The interface was successfully installed.@retval EFI_INVALID_PARAMETER The PpiList pointer is NULL.@retval EFI_INVALID_PARAMETER Any of the PEI PPI descriptors in the list do not have theEFI_PEI_PPI_DESCRIPTOR_PPI bit set in the Flags field.@retval EFI_OUT_OF_RESOURCES  There is no additional space in the PPI database.**///使特定的PEIM向PEI foundation注册接口
EFI_STATUS
EFIAPI
PeiServicesInstallPpi (IN CONST EFI_PEI_PPI_DESCRIPTOR  *PpiList //指向调用者应安装的接口列表的指针);

 所提供的服务由EFI_PEI_PPI_DESCRIPTOR这个数据结构通过PEIM向PEI foundation进行描述,这个数据结构的原型为

// The data structure through which a PEIM describes available services to the PEI Foundation.
typedef struct {// This field is a set of flags describing the characteristics of this imported table entry.// All flags are defined as EFI_PEI_PPI_DESCRIPTOR_***, which can also be combined into one.UINTN       Flags;// The address of the EFI_GUID that names the interface.EFI_GUID    *Guid;// A pointer to the PPI. It contains the information necessary to install a service.VOID        *Ppi;
} EFI_PEI_PPI_DESCRIPTOR;

完整的 MyHelloWorldInstallPPI模块的源文件为:

#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Pi/PiHob.h>
#include <Pi/PiPeiCis.h>EFI_GUID gMyHelloWorldPEIGUID = { 0xbdb38129, 0x4d65, 0x39f4, { 0x72, 0x12, 0x68, 0xcf, 0x5a, 0x19, 0xa, 0xf8 }};//定义了一个DEBUG_ERROR级别的信息打印函数,
EFI_STATUS
EFIAPI
PrintHelloMsg (IN CHAR16 * Msg //定义了一个输入参数,类型为CHAR16的指针)
{DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] PrintHelloMsg : %s \n",Msg));return EFI_SUCCESS;
}/*
typedef的作用是给已有的数据类型或结构体定义一个新的名称
函数指针:指向函数的指针变量
int maxValue (int a, int b) {return a > b ? a : b;
}    int (*p)(int, int) = NULL;  //定义一个与maxValue兼容的指针
p = maxValue;
p(20, 45);  //通过指针调用
*/
typedef EFI_STATUS (EFIAPI * PRINTMSG)(CHAR16 *Msg);//给已有的数据类型定义了一个新的名称PRINTMSG,原来的数据类型为函数指针类型EFI_STATUS (EFIAPI *)(CHAR16* Msg)
typedef struct _EFI_HELLOWORLD_PROTOCOL  {PRINTMSG PrintMsg;
}EFI_HELLOWORLD_PROTOCOL;//定义了一个结构体,结构体成员为函数指针PrintMsg,并使用typedef将这个结构体的别名为EFI_HELLOWORLD_PROTOCOL//ppi实例
EFI_HELLOWORLD_PROTOCOL mHelloWorldPpi = {PrintHelloMsg
};//定义了一个EFI_HELLOWORLD_PROTOCOL类型的全局变量,并赋初值为PrintHelloMsg(即指向上面的打印函数)EFI_PEI_PPI_DESCRIPTOR     mPpiListCodePpi = { //EFI_PEI_PPI_DESCRIPTOR是一个数据结构,可以向PEIfoundation描述能够提供的服务(EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), //EFI_PEI_PPI_DESCRIPTOR_***这样flag的组合,| 表示按位或&gMyHelloWorldPEIGUID, //GUID, PPI的名字&mHelloWorldPpi //ppi实例
};//在这里定义了一个结构体变量mPpiListCodePpi,并进行初始化//安装PPI,指的是把一个或多个PPI注册到PEI foundation中
EFI_STATUS
EFIAPI
MyHelloWorldInstallPPIEntry(IN       EFI_PEI_FILE_HANDLE  FileHandle,IN CONST EFI_PEI_SERVICES     **PeiServices //指向EFI_PEI_SERVICES table的间接指针
)
{EFI_STATUS                              Status = EFI_SUCCESS;DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] MyHelloWorldInstallPPIEntry Start..\n"));PeiServicesInstallPpi  (&mPpiListCodePpi);DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] MyHelloWorldInstallPPIEntry End..\n"));return Status;
}

2、编写InstallPPI的inf文件

3、编写LocatePPI的源文件:

 PeiServicesLocatePpi服务的作用让PEIM发现接口的给定实例,其原型为:

EFI_STATUS
EFIAPI
PeiServicesLocatePpi (IN CONST EFI_GUID              *Guid, //指向需要被查找的GUIDIN UINTN                       Instance, //所需接口的第几个实例IN OUT EFI_PEI_PPI_DESCRIPTOR  **PpiDescriptor  OPTIONAL, //指向EFI_PEI_PPI_DESCRIPTOR实例的指针IN OUT VOID                    **Ppi //指向接口实例的指针);
#include <uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/PeimEntryPoint.h>
#include <Library/PeiServicesLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Pi/PiHob.h>EFI_GUID gMyHelloWorldPEIGUID = { 0xbdb38129, 0x4d65, 0x39f4, { 0x72, 0x12, 0x68, 0xcf, 0x5a, 0x19, 0xa, 0xf8 }};typedef EFI_STATUS (EFIAPI * PRINTMSG)(CHAR16 *Msg);
typedef struct _EFI_HELLOWORLD_PROTOCOL  {PRINTMSG PrintMsg;
}EFI_HELLOWORLD_PROTOCOL;//ShellCEntryLib call user interface ShellAppMain
EFI_STATUS
EFIAPI
MyHelloWorldLocatePPIEntry(IN       EFI_PEI_FILE_HANDLE  FileHandle,IN CONST EFI_PEI_SERVICES     **PeiServices
)
{EFI_STATUS  Status = EFI_SUCCESS;EFI_HELLOWORLD_PROTOCOL *mHelloWorldPpi = NULL;DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] MyHelloWorldLocatePPIEntry Locate PPI Start..\n"));
//定位PPI接口Status = PeiServicesLocatePpi (&gMyHelloWorldPEIGUID, //我们要查找的PPI的GUID0,NULL,(VOID **)&mHelloWorldPpi //指向PPI实例指针的指针);if (EFI_ERROR(Status)){DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] MyHelloWorldLocatePPIEntry Locate PPI Fail..%r\n",Status));return Status;}mHelloWorldPpi->PrintMsg(L"2019 CSDN Locate PPI Hello World ...\n");DEBUG ((EFI_D_ERROR, "[MyHelloWorldPPI] MyHelloWorldLocatePPIEntry Locate PPI End..\n"));return Status;
}

4、编写LocatePPI的inf文件

编译整个包,并将生成的OVMF.fd文件加载到qemu中,其

注意这里的模块是PEIM,无法在qemu里运行其二进制文件

制作运行qemu的脚本,新建一个qemurun.bat文件,将以下内容保存在其中:

@echo off
chcp 65001
"E:\Program Files\qemu\qemu-system-x86_64.exe" -bios "E:\UEFIWorkSpace\Build\OvmfX64\DEBUG_VS2017\FV\OVMF.fd" -M pc -m 256 -cpu "qemu64" -boot order=dc -serial stdio -net none -usbdevice disk:HDD_BOOT.img

(注:根据自己的qemu的安装位置和fd文件的位置进行更改) 

然后运行命令:

qemurun.bat | findstr MyHelloWorldPPI

 其运行结果如下

E:\UEFIWorkSpace\edk2>qemurun.bat | findstr MyHelloWorldPPI
WARNING: Image format was not specified for 'HDD_BOOT.img' and probing guessed raw.Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.Specify the 'raw' format explicitly to remove the restrictions.
[MyHelloWorldPPI] MyHelloWorldInstallPPIEntry Start..
[MyHelloWorldPPI] MyHelloWorldInstallPPIEntry End..
[MyHelloWorldPPI] MyHelloWorldLocatePPIEntry Locate PPI Start..
[MyHelloWorldPPI] PrintHelloMsg : 2024 CSDN Locate PPI Hello World ...
[MyHelloWorldPPI] MyHelloWorldLocatePPIEntry Locate PPI End..

 结果总结:

MyHelloWorldInstallPPI模块通过PeiServicesInstallPpi提供了mPpiListCodePpi服务(通过 EFI_PEI_PPI_DESCRIPTOR来指定具体的实例),而MyHelloWorldLocatePPI模块通过PeiServicesLocatePpi获取了mPpiListCodePpi对象,通过访问对象的成员函数即可完成对PEIM驱动MyHelloWorldInstallPPI资源的访问。

(注意,要想在运行的时候输出以上DEBUG信息,需要在编译包的时候加上DEBUG_ON_SERIAL_PORT) 

参考博客:https://blog.csdn.net/xiaopangzi313/article/details/89602882

这篇关于UEFI开发——编写一个简单的PPI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1116006

相关文章

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Python使用smtplib库开发一个邮件自动发送工具

《Python使用smtplib库开发一个邮件自动发送工具》在现代软件开发中,自动化邮件发送是一个非常实用的功能,无论是系统通知、营销邮件、还是日常工作报告,Python的smtplib库都能帮助我们... 目录代码实现与知识点解析1. 导入必要的库2. 配置邮件服务器参数3. 创建邮件发送类4. 实现邮件

python编写朋克风格的天气查询程序

《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI... 目录工具介绍工具使用说明python脚本内容如何运行脚本工具介绍这个天气查询工具是一个基于 Pyt

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

基于Python开发一个有趣的工作时长计算器

《基于Python开发一个有趣的工作时长计算器》随着远程办公和弹性工作制的兴起,个人及团队对于工作时长的准确统计需求日益增长,本文将使用Python和PyQt5打造一个工作时长计算器,感兴趣的小伙伴可... 目录概述功能介绍界面展示php软件使用步骤说明代码详解1.窗口初始化与布局2.工作时长计算核心逻辑3