Step to UEFI (275)UEFI 创建内存虚拟盘

前面介绍过在 UEFI 下创建内存虚拟盘的操作,这次介绍如何创建包含需要内容的内存虚拟盘。生成的虚拟盘可以在没有硬盘的情况下充当临时文件的存放位置。当然如果关机或者断电后,盘中内容会消失。

第一步,根据【参考1】制作一个磁盘镜像VHD,这里出于体积考虑,制作了一个 64MB 的FAT32 空盘;

第二步,上面制作磁盘镜像大部分内容都是 0x00(可以看作是以512Bytes为单位的稀疏矩阵),因此我们还需要一个工具提取文件中的非零内容保存起来,这样能够有效降低镜像尺寸。使用 C# 编写代码如下。简单的说就是以512Bytes为单位读取文件,然后检查512 bytes 中是否为全0.如果不是就记录下来保存到文件中。每一个项目是由 4 Bytes 记录加一个 512Byte的数组构成。特别注意 VHD 镜像文件末尾包含了一个文件头,我们这里会对文件大小取整,对于末尾的文件头不会进行处理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace ConsoleApp7
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Count() == 0) {
                Console.WriteLine("Please input file name!");
                Console.ReadKey();
                Environment.Exit(0);
            }

            if (File.Exists((args[0] + ".raw"))) {
                File.Delete(args[0] + ".raw");
            }
            FileStream fs = new FileStream(args[0], FileMode.Open);
            FileStream RawFs = new FileStream(args[0]+".raw", FileMode.CreateNew);
            byte[] array = new byte[512];
            UInt32 Data;
            Boolean EmptyMark;

            // 写入 Disk Image 的Size (以 1MB 为单位)
            Data = (UInt32) fs.Length / 1024 / 1024;
            RawFs.Write(BitConverter.GetBytes(Data), 0, 4);
            
            // 处理整数以内的磁盘,例如:扫描 64MB 以内的内容生成及镜像
            while (fs.Position < fs.Length / 1024 / 1024 * 1024 * 1024) {
                EmptyMark = true;
                fs.Read(array, 0, array.Length);
                for (int i = 0; i < array.Length; i++)
                {
                    if (array[i] != 0x00)
                    {
                        EmptyMark = false;
                        break;
                    }
                }
                if (EmptyMark==false)
                {
                    Data = (UInt32)(fs.Position / 512 - 1);
                    RawFs.Write(BitConverter.GetBytes(Data),0,4);
                    RawFs.Write(array, 0, array.Length);
                    Console.WriteLine("{0}", Data);
                }
            }
            RawFs.Close();
            Console.WriteLine("Done!");

        }
    }
}

例如:64MB的FAT32 空盘,经过上述操作后会变成6K 大小。

第三步,编写Shell 下的 UEFI Application,创建内存虚拟盘,然后读取前述生成的文件,将内容放在虚拟盘中。这样就得到了一个带有文件系统的内存虚拟盘。代码如下:

#include <Library/BaseLib.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/ShellCEntryLib.h>
#include <Protocol/RamDisk.h>
#include <Protocol/DevicePathToText.h>
#include <Protocol/HiiDatabase.h>
#include <Protocol/HiiPackageList.h>
#include <Protocol/HiiImageEx.h>
#include <Protocol/PlatformLogo.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/ShellLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>

//DO NOT REMOVE IMAGE_TOKEN (IMG_LOGO)

extern EFI_BOOT_SERVICES         *gBS;

EFI_STATUS
EFIAPI
UefiMain (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
)
{
	EFI_STATUS               Status;
	EFI_RAM_DISK_PROTOCOL    *MyRamDisk;
	EFI_FILE_HANDLE   		FileHandle;
	UINTN	tmp, DiskSize,SectorIndex;
	UINT64                   *StartingAddr,Position;
	UINT8                    Sector[512];
	UINTN					 ImageFileSize;
	EFI_DEVICE_PATH_PROTOCOL *DevicePath;

	// 磁盘镜像文件
	CHAR16	*DiskImage=L"DiskImage.BIN";
	// 如果磁盘镜像不存在,报错退出
	if (ShellFileExists(DiskImage)!=EFI_SUCCESS)
	{
		Print(L"Couldn't find 'DiskImage.bin'\n");
		return EFI_INVALID_PARAMETER;
	}

	// 打开磁盘镜像文件
	Status = ShellOpenFileByName(
	             DiskImage,
	             (SHELL_FILE_HANDLE *)&FileHandle,
	             EFI_FILE_MODE_READ,
	             0);
	if(Status != RETURN_SUCCESS)
	{
		Print(L"OpenFile failed!\n");
		return EFI_INVALID_PARAMETER;
	}


	// Look for Ram Disk Protocol
	Status = gBS->LocateProtocol (
	             &gEfiRamDiskProtocolGuid,
	             NULL,
	             &MyRamDisk
	         );
	if (EFI_ERROR (Status))
	{
		Print(L"Couldn't find RamDiskProtocol\n");
		return EFI_ALREADY_STARTED;
	}

	tmp=4;
	Status = ShellReadFile(FileHandle,&tmp,&DiskSize);
	Print(L"Disk size %dMB\n",DiskSize);
	DiskSize=DiskSize*1024*1024;

	//Allocate a memory for Image
	StartingAddr = AllocateReservedZeroPool	((UINTN)DiskSize);	 
	if(StartingAddr==0)
	{
		Print(L"Allocate Memory failed!\n");
		return EFI_SUCCESS;
	}

	ShellGetFileSize(FileHandle,&ImageFileSize);
	Position=0;
	Print(L"File size %d\n",ImageFileSize);

	while (Position<ImageFileSize)
	{
		tmp=4;
		Status = ShellReadFile(FileHandle,&tmp,&SectorIndex);
		if (Status!=EFI_SUCCESS)
		{
			break;
		}
		Print(L"Sector index %d\n",SectorIndex);
		tmp=512;
		Status = ShellReadFile(FileHandle,&tmp,&Sector);
		if (Status==EFI_SUCCESS)
		{
			//Print(L"Read success %d\n",(UINT8*)StartingAddr+SectorIndex*512);
			CopyMem((UINT8*)StartingAddr+SectorIndex*512,&Sector,tmp);
		}
		ShellGetFilePosition(FileHandle,&Position);
		//Print(L"postion %d\n",Position);
	}

	//
	// Register the newly created RAM disk.
	//
	Status = MyRamDisk->Register (
	             ((UINT64)(UINTN) StartingAddr),
	             DiskSize,
	             &gEfiVirtualDiskGuid,
	             NULL,
	             &DevicePath
	         );
	if (EFI_ERROR (Status))
	{
		Print(L"Can't create RAM Disk!\n");
		return EFI_SUCCESS;
	}
	ShellCloseFile(&FileHandle);

	return EFI_SUCCESS;
}

基本思路就是:打开镜像文件读取4 Bytes,然后创建这个大小的 Memory Disk。接下来读取 4 Bytes的扇区位置,然后再读取512字节的扇区内容,将这个内容存放在内存中的对应位置。

在Shell 下执行这个程序后,使用 map -r 即可看到新生成的内存盘。

UEFI 代码和编译后的EFI 文件,同时内置了一个64MB的磁盘镜像。

前面提到的 Windows 下的磁盘镜像扫描工具。内置了一个 64MB的空磁盘镜像。

参考:

1. https://www.lab-z.com/vt/

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注