UEFI开发探索51 – UEFI下的打印函数

原计划的50篇博客,终于完成了。可惜的是,现有的篇幅无法把最初设想的内容全部覆盖。因此,开发探索系列还会继续写下去,既然原定目标50篇已经达成,新的目标篇数就不设定了。只针对我感兴趣的各方面的内容,继续探索。

在日常的开发中,总会用到各种打印(print)函数。UEFI下的打印函数,其格式有点奇怪,和Windows及Linux下的print函数不大相同。而且又涉及到Ascii字符和Unicode字符的支持,在使用中总是犯一些小错,让人很是恼火。

因此,为了方便后面的开发,我准备将相关的打印函数整理出来,以备参考。

1OutputString()

最基本的打印输出函数,其他Print函数都是基于此函数构建的。其函数原型为:

typedef
EFI_STATUS
(EFIAPI *EFI_TEXT_STRING) (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
*This,//指向    
                                 //EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
实例的指针
IN CHAR16 *String //Null结尾的字符串
);

此函数向输出设备写入字符串,显示在当前光标处,是最基本的输出机制。

示例如下:

gST->ConOut->OutputString(gST->ConOut,L”Hello, UEFI World!\n\r”);

这是Simple Text Output Protocol最核心的函数,配合此Protocol的其他函数,比如SetAttribute等,可以实现彩色背景和彩色字体。在之前的博客中曾经展示过,可以做出与众不同的命令行式Sell程序。

2 格式化输出

PrintLib提供了格式化输出的支持函数,它支持所有Unicode和ASCII字符串。在Package的dsc文件中,一般都会提供这个库的编译。如下:

PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf

UEFI的格式化方式,与ANSI C的标准不尽相同,在使用的时候,很容易造成概念混淆。

格式化语法如下:

%[flags][width][.precision]type

继续阅读“UEFI开发探索51 – UEFI下的打印函数”

27 total views, 12 views today

UEFI开发探索50 – UEFI与网络2

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

3 在VirtualBox中使用网络

在UEFI开发探索系列博客的第48篇中,我介绍了如何在VirtualBox中搭建UEFI Shell。本节就在这篇博客的基础上,让VirtualBox下的UEFI Shell有访问网络的功能。

1) 安装网络驱动和网络协议驱动

我使用的是VirtualBox6.1.4,其虚拟网卡为Intel Pro/1000MT Desktop。网卡驱动下载地址为:

https://downloadcenter.intel.com/download/27539/Ethernet-Intel-Ethernet-Connections-Boot-Utility-Preboot-Images-and-EFI-Drivers

下载版本为22.10的PREBOOT.exe,双击安装。将目录/APPS/EIF/EFIx64下的驱动E3522X2.EFI拷贝到虚拟机的硬盘中。同时,将之前编译好x64的Ipv4网络协议驱动(MdeModulePkg)也拷贝到虚拟机的硬盘中。

启动虚拟机,进入UEFI Shell,执行如下命令,加载网卡驱动和网络协议驱动:

Shell>fs0:
fs0:>load E3522X2.EFI
fs0:> load SnpDxe.efi MnpDxe.efi ArpDxe.efi Ip4Dxe.efi VlanConfigDxe.efi Udp4Dxe.efi Dhcp4Dxe.efi Mtftp4Dxe.efi Tcp4Dxe.efi

2) 配置网卡。

使用ifconfig命令设置:

fs0:\>ifconfig -s eth0 dhcp

检查网络配置情况:

图1 VirtualBox的UEFI Shell中查看IP
继续阅读“UEFI开发探索50 – UEFI与网络2”

62 total views, 2 views today

UEFI开发探索49 – UEFI与网络1

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

UEFI提供了非常完整的TCP/IP网络协议栈,开发人员甚至可以在UEFI上开发自己的Web服务器。其网络协议栈如图1所示:

图1 UEFI的网络协议栈
继续阅读“UEFI开发探索49 – UEFI与网络1”

90 total views, 1 views today

UEFI开发探索48 – 搭建UEFI Shell环境

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

做UEFI的实验,32位的程序可以使用TianoCore的模拟环境进行测试。64位的程序,可以使用启动盘在实际的机器上测试,也可以使用虚拟机来测试。

制作启动盘很简单,不过,我之前保存的启动文件丢失了,正好重新做一遍,把过程记录下来。

1 制作UEFI BIOS下的启动盘

启动文件可以通过编译ShellPkg来得到。打开VS命令行,进入EDK的目录,执行edksetup.bat后,输入以下命令:

build -a IA32 -a X64 -p ShellPkg\ShellPkg.dsc -b RELEASE

在EDK的Build\Shell\RELEASE_VS2015x86目录下,会生成32位和64位的完整shell执行文件。

然后执行以下步骤:

1) 将U盘格式化为FAT32(FAT、FAT16也可以,现在很少使用了)格式;

2) 在U盘的根目录下建立efi\boot文件夹;

3) 将刚才生成的两个32位和64位shell执行文件(名称都是Shell.efi),分别改名为bootx32.efi和bootx64.efi,拷贝到U盘的efi\boot目录下。

做好的U盘插到计算机上,开机的时候选择从U盘启动(每款BIOS不大一样,常见的可以按F11选择启动项,或者进入Bios Setup设置从U盘启动),即可进入UEFI shell了。

继续阅读“UEFI开发探索48 – 搭建UEFI Shell环境”

102 total views, 5 views today

UEFI开发探索47 – UEFI上移植GUILite

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

上一篇的博客中,已经搭建了C++的编程框架,虽然不是所有C++特性都支持了,比如new和delete,以及虚析构函数等,但是用来移植GuiLite已经足够了。下面就开始着手这一工作。

1 代码选择

GuiLite的例子比较丰富,从控件到动画、甚至3D的实现,都可以找到。浏览了一圈后,我选择从HelloTimer开始动手。

这是在STM32F103上运行的代码:

图1 STM32F103例程: HelloTimer
继续阅读“UEFI开发探索47 – UEFI上移植GUILite”

125 total views, 5 views today

UEFI开发探索46 – UEFI支持C++

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

由于GUILite是由C++开发的,将其移植到UEFI下,意味着代码必须用C++来编写。

以下的内容,大部分来自于《UEFI原理与编程》第10章,将需要的代码整合到了我自己的框架中。有些书中没有遇到的小问题,博客中也给了解决办法。

1 C与C++之于UEFI

UEFI中的程序遵循的是C语言的规则,大部分时候应该用模块化的思维方式去理解代码。在日常开发的时候并没有什么问题,何况C语言比C++更适合直接与硬件打交道的场景。不过,遇到稍大点的GUI应用,或者移植遗留的C++代码时,就不得不采用C++了。比如这次的GuiLite的移植工作。

在windows下,开发所用的工具是Visual Studio,编译器cl本身就是支持C++的。因此,我们所要做的工作是让UEFI的入口函数能够“认识”C++的代码。

C++与C的主要区别在于函数名、数组名等,在经过编译后会不同。这是名字修饰的过程,大部分编译原理的书籍中都有描述过。因此,如果直接用C的代码去调用C++的库,会导致链接失败的。

为了支持C++,EDK2在后续的版本中,也逐渐地添加了一些支持。比如在生成的中间文件AutoGen.h中,自动添加了extern “C”,解决了中间文件的名字修饰问题。

我的开发环境是UDK2018+VS2015,在Win10上进行开发的。

2 支持基础的类

基本上进行以下四个步骤,就可以支持C++的类的功能了。

1) 解决名字修饰问题

这个问题比较简单,只需要在C++源文件中,将C语言的头文件用extern “C”包含起来就可以了。如图:

图1 c与C++的名字修饰问题
继续阅读“UEFI开发探索46 – UEFI支持C++”

118 total views, 3 views today

UEFI开发探索45 – GuiLite概览

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

在开始这个探索系列的时候,我就计划在UEFI上移植一个完整的GUI库。

前面开发各种图形编程、特效实现的代码,其实有很大一部分来自于我之前另外一个项目-Foxdisk。在这个项目中,某种程度上实现了任务的切换,可以看做一个小型的、带有图形显示的shell界面。

不过,Foxdisk中的键盘处理是完全脱离于图形的,而且也没有实现鼠标的处理。简而言之,这是一个很松散的、模块化的GUI库。与我期望的,类似于MFC、QT、JUCE之类的库相差甚远。

UEFI不是一个完整的操作系统,所选用的GUI库实际上最好偏向于嵌入式的。在简单考察了几个开源的GUI库后,包括LearingGUi、GuiLite、littlevgl等,我选择了从GuiLite开始着手这项工作。

一方面是因为其代码量看起来不大,6000行左右,花费的时间应该不会太多;二是这款开源库是国人开发的,我也加入了作者建的QQ群,有什么问题可以很方便地请教。

1 GUILite类的组织

按照其文档介绍,GuiLite只做两个工作:界面元素管理和图形绘制。而图形绘制不依赖于界面管理,可以独立存在,以应对需要移植到资源有限的单片机环境。

其类的组织图如下:

图1 GUILite类图
继续阅读“UEFI开发探索45 – GuiLite概览”

222 total views, 2 views today

Foxdisk12 – 图形显示1

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

最近在写UEFI博客的时候,遇到些阻碍。我想移植一款开源的GUI到UEFI下,目前还没有找到。不过,博客开天窗这么久,总是不像话,所以回来继续写Foxdisk的博客了。

Foxdisk的博客更新较慢,主要是觉得这些都是Legacy BIOS下的东西,可能想了解的人不多,我也是随性而写,并不制定计划。

使用Foxdisk3的代码,很难演示如何进行图形编程。因此,我还是使用平常调试用的程序框架来说明。所有的代码,都是在DOS下实验通过,然后再移植到Foxdisk3的代码中去的。

特别说明,Foxdisk3只针对Legacy BIOS,所有访问硬盘、图形显示、键盘访问以及时钟中断的构建,都是在Legacy BIOS的架构下进行的。如想了解UEFI BIOS下如何编写类似代码,请移步我的另一博客-《UFEI开发探索系列》

1 编译和运行环境

我以前都是在虚拟机下调试,使用的是VirtualPC2.5以及Bochs,偶尔也用过Virtual BOX和Vmware构建。每种环境都各有优缺点,比如Bochs的调试环境非常完备,不过显示的时候颜色有时候会很奇怪。

如果只是演示图形编程的话,我觉得DosBox比较好:启动快、DOS环境模拟得很好。我使用的是DosBox v0.74-2,到网上去找即可,免费的。

图1 DosBOX
继续阅读“Foxdisk12 – 图形显示1”

1,405 total views, 2 views today

UEFI开发探索44 – 龙芯下的UEFI App和Option ROM

请保留-> 【原文: http://yiiyee.cn/blog/author/luobing/】

年初的时候,不少客户都在问,国产的电脑上是不是能用隔离方案?

工程师做了一番调查,大部分客户用的都是龙芯的电脑(3A3000)。硬件上来说,主要是看PCI/PCIE的支持情况;软件上,BIOS需要支持我们的Option ROM,操作系统上的应用程序也得重写。

总的来说,工作量不算大,也不算小,由此开始了我们几个月的产品适配过程。

图1 调试用的机器
继续阅读“UEFI开发探索44 – 龙芯下的UEFI App和Option ROM”

631 total views, 2 views today

UEFI开发探索 – 间幕

嗯,最近把博客移动到了CSDN上了,主要是那边比较方便查看博客:十几篇文章的标题可以在一个页面上显示。

我们的博客网站就没那么方便啦,还是比较适合个人浏览。我们俩也没那么多时间去调整,就这么继续写下去吧。

预计中的50篇,马上就要接近尾声了。我正在搜肠刮肚地寻找课题,可是感觉每个课题都特别大,比如写个uefi下的磁盘管理工具、写个类似grub的多引导工具、移植个GUI库过来等等。

都很有趣,也都很…费时间。

中年程序员大叔的时间可是很宝贵的,能压缩的时间只剩下睡觉了。再这么下去,仿佛去世多年的爷爷在河的那边正向我招手呢,模模糊糊的能看见…

总之,有兴趣的话,可以去CSDN上关注下:

https://me.csdn.net/luobing4365

当然,这里一直都会是我的主战场,不定期地更新自己的技术点滴或者感想。

最后,上一张老家的风景图。国庆的时候带娃去明月山了,温泉泡得很舒服,爬山很好玩。明月山和羊狮慕的栈道,挂在山腰,走完要两个多小时,非常有意思。

当然,不愧是AAAAA级景区,价格也不便宜,虽然大部分都是老哥付的钱^^

230 total views, 2 views today

UEFI开发探索43 – Protocol的使用2

(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )

今天来探索上次提出的第三个问题:如何产生Protocol?

在常看的书《UEFI原理与编程》中,实际上已经介绍了如何开发UEFI服务了。他以视频解码为例,提供了一个完整的解码库。

目前对视频解码没有什么兴趣,因此,这篇内容对我来说,多余的枝节太多了。我准备构建一个比较简单的框架型代码,可以用来在屏幕上画几何图形,以熟悉如何开发Protocol。

1 UEFI Driver

相比于Windows driver,UEFI driver简单很多,大致可以分为两类:符合UEFI驱动模型的驱动和不遵循UEFI驱动模型的驱动,如图:

图1 UEFI Images – EDKII Driver Writer’s Guide section3.7
继续阅读“UEFI开发探索43 – Protocol的使用2”

451 total views, 9 views today

UEFI开发探索42 – Protocol的使用1

(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )

虽然一直使用各种Protocol来实现需要的程序功能,但对其背后的原理、实现方法,一直都比较模糊。我奉行的是“先用再说”的实用主义,正好周末有点闲暇,探究一下对Protocol理解模糊的地方。

图1 Protocol的构成

如图为Protocol的结构图,摘自于UEFI Spec 2.8 page 45。

我想弄清楚的问题如下:

1) 如何使用Protocol服务?
2) Protocol这种机制在UEFI中是如何实现的?
3) 如何实现一个Protocol?

继续阅读“UEFI开发探索42 – Protocol的使用1”

418 total views, 3 views today

UEFI开发探索41 – Event、Timer和任务优先级

(请保留->发布地址: http://yiiyee.cn/blog/author/luobing/ )

作为一个底层的支持系统,UEFI没有支持中断。如果想支持异步操作,只能通过事件(Event)来实现。

在开发Foxdisk的过程中,也遇到需要同时处理的事件。比如提示用户输入的闪烁光标、自动显示的系统时间等,我是采用了时钟中断(int 1Ch)的方式来实现的,是段很有意思的程序。

不过,我只是简单地将需要的功能堆砌在int 1Ch中实现,并没有完整地实现多任务间的互斥,是一种“伪多任务”的实现。那么,UEFI中是怎么来支持多个任务同时执行呢?

1 支持的服务函数

图1为相关的服务函数,总共10个:

图1 事件相关服务函数
继续阅读“UEFI开发探索41 – Event、Timer和任务优先级”

884 total views, 4 views today

UEFI开发探索40 – 构建自己的Package

前段时间在Linux下开发UEFI程序,发现以前写的AppPkg的32位程序没法编译,无法在模拟环境下测试执行程序。

我当时就想脱离AppPkg,自己构建Package。当然,StdLib的库不能使用了,也不能以main()函数为入口。我觉得这都不是什么大事,毕竟平常构建的Option ROM代码也不能使用这些。

说干就干,顺便把各种类型文件的知识点过一遍。

1 编译框架

EDKII的编译系统是基于Python和C的代码构建的,可以跨平台编译。也可运行在不同的CPU架构上,比如X86和ARM,最近我们在做针对龙芯MIPS的软件,乐见UEFI发展版图的扩大。

图1 EDKII 工作流程
继续阅读“UEFI开发探索40 – 构建自己的Package”

592 total views, 2 views today

UEFI开发探索39 – Ubuntu 16.04下用gdb建立UEFI调试环境

准确地说,应该是在Ubuntu 16.04下,使用Qemu模拟UEFI启动环境,同时配合Intel UDK Debugger tool和gdb建立的X64调试环境。

使用的是Qemu和OvmfPkg,类似于之前使用windbg在Windows下搭建调试环境,这次换为在Linux下搭建了。正是上一篇博客留下的题目。

1 参考资料

如何在Linux下搭建调试环境,网上的资料不多,特别是如何让gdb挂上UEFI启动环境,我摸索了几天。

可供参考的资料有《UDK_Debugger_Tool_User_Manual_V1.11.pdf》,在网站https://firmware.intel.com/develop/intel-uefi-tools-and-utilities/intel-uefi-development-kit-debugger-tool上下载,其中第六章开始介绍如何在Linux下搭建调试环境。

以及开源项目Slim Bootloader: https://slimbootloader.github.io/index.html。它在调试的时候使用了UDK Debugger Tool,搭建调试环境的方法可用来类比。

图1 Slim Bootloader’s LOGO
继续阅读“UEFI开发探索39 – Ubuntu 16.04下用gdb建立UEFI调试环境”

737 total views, 5 views today

UEFI开发探索38 – Ubuntu下编译AppPkg杂谈

上一篇博客中,在编译AppPkg的时候,遇到了问题,编译的时候出错。错误的提示在上一篇博客中贴出来了,这里不再贴出。针对此问题,我查找了一些资料,做了若干实验,姑且以杂谈的形式记录下来。

1 EADK

为了方便使用标准的C库,EDKII中提供了开发包:EDK II Application Development Kit,简称为EADK。它最早脱胎于EFI_ToolKit,是Intel推广EFI的范例程序包。

随着UEFI的法发展,原来的架构无法统一,EFI_ToolKit的代码被分别整合到各个Package中了。其中的python和Application Development Kit就由EADK接替,并维护着。不过,从github上的日期来看,最后维护的时间也是2015了,是不是后面放弃了?

不管如何,它能很方便的用来构建Uefi程序,也能直接使用熟悉的C库函数,之前博客中的大量代码都使用了它。 它包含了三个Package:

图1 EADK
继续阅读“UEFI开发探索38 – Ubuntu下编译AppPkg杂谈”

642 total views, 8 views today