1 EFI文件系统现状
无论是EDK还是最新的EDKII,都只支持FAT,并没有对LINUX系统原生态文件系统EXT进行支持。我刚写了EXT驱动没多长时间,趁着没有忘记,就把整个过程写下来,俗话说的好,好记性不如赖笔头。
2 EXT文件系统结构
我们来看一张图:
上面的图为一个磁盘分区的结构。
最起始的部分是BootBlock,固定占有1024字节,只是磁盘厂商传承下来的产物,对于我们的文件系统来说并无帮助。
Block Group,EXT为了便于管理磁盘,便将磁盘划分为不同的块组。因为操作系统不是以扇区为单位进行磁盘读取,而是以逻辑块为基本单位进行读取,在BITMAP中每一个BIT代表一个逻辑块,所以可以将逻辑块乘以8组成一个块组。这样就方便了磁盘管理和读取。
SuperBlock,占有1024字节,磁盘格式化后,便维护着这样一个结构。这个结构特别重要,它指示了整个文件系统的Block和Inode的大小、Block和Inode的数量、Block和Inode的已使用和未使用数量、文件系统版本号、最近一次挂载时间等等。
Group Description,组描述符,整个文件系统只使用Block Goup0的组描述符即可,其它不用访问,只是数据的一个拷贝。它记录着各个BLOCK GROUP的BLOCK BITMAP和INODE BITMAP的起始位置。
DataBlock Bitmap:本组数据块使用情况,使用过,其BIT设为1,空闲则为0。
Inode Bitmap:本组Inode Table使用情况,其用法与DataBlockBitmap相同。作为EFI生BIOS工程师,我们只负责读取文件系统,并不会去往磁盘上写数据,所以这两个结构可以不去管它们。
Inode Table:每个目录或文件都占用一个Inode,用Inode Table来描述此目录或文件的使用情况及属性。对于EFI BIOS工程师来说,我们更注重其中的数据块及占有的数据块数量以及一级指针,二级指针三级指针几个字段,因为这关系到我们读取文件数据。文件大小不一般来讲,并不是正好是BLOCKSIZE整数倍大小,那怎么可以读到大小正确数据值?这里面还有一个SIZE大小的字段,整个数据BLOCK的最后一个BLOCK要根据这个SIZE字段来截断。
DataBlock:我们解析目录和文件都在这里。如果是目录,就有一个DIR_ENTRY的结构来解析此数据块。
以上便是理论部分。具体应该怎么做呢?下面我便粗略地描述一下读文件方式,因为作为EFI BIOS工程师来说,更关心如何读取文件。
3 读取文件方式
我们想要读取一个文件,首先要找到其inode号,然后根据inode号找到inodetable中对应的inode结构,则inode结构找到数据块及大小,然后将其读出。
我们来看一下红帽的grub.efi读取。当然它是FAT文件系统,但我们先假设其是EXT文件格式的。其路径为:\EFI\redhat\grub.efi
首先找根目录\,其inode号固定为2,其肯定在Group Block0内,找到其inode结构,然后找到其数据块,根据DIR_ENTRY检索EFI目录项。
由EFI目录项,可以找到其占用的inode号,同理可以找到其对应的inode结构,进而找到其数据块,分析并得到redhat目录项。
同上面两个步骤一样,再次循环得到grub.efi文件项。
在grub.efi文件项中可以找到其对应的inode号,由inode号找到其对应的inode结构,进而找到其占用的数据块和文件大小,将其所有数据块读出,并由SIZE确定最后一个数据块占用大小,其所有数据块最终组成一个文件就是grub.efi文件的数据。
4 主要数据结构
读取文件所用到的结构罗列如下:
超组块,块描述符,Inode结构,文件目录项,其结构可以自行在网上查找。
5 具体做法
(1)建一EXT驱动,因为其会为很多控制器提供驱动,所以类型选择UEFI_DRIVER。
(2)在驱动入口处,安装binding协议。
(3)在Supported函数中,规定非装有DISKIO和BlockIO的控制器不得进入。
(4)在Start函数中,读取superblock,并根据其字段判断该分区是否支持EXT文件系统,若是便将EXT解析函数封装为一个Ext协议,并安装在该控制器上。
(5)在LoadImage中,在FAT读取程序段下面,添加EXT数据读取程序段。
(6)在BootOption枚举函数中,检索各个装有Ext协议的控制器是否有我们所需要的BootLoader,如果有,便为其注册BoorOrder选项。