UEFI Framework - 4 [ EFI OS Loader ]


在這個章節我會討論關於OS Loader的細節,OS Loader是一個特別的 EFI Application,
主要的任務是擔任 platform firmware 跟 the operating system runtime中間的橋梁,也就是將板子帶到開進OS的狀態。

  1. OS Loader要決定它自己要從那裡被戴入進 System Memory
  2. OS Loader要知道OS是存放在那裡,通常是會在HDD裡,透過Block I/O Read/Write,接著會實做或戴入File System Driver存取OS的檔案
  3. 必需實作 memory map of physical memory,讓OS知道在Memory這邊 實際上可以管理的東西。 OS loader必須使用EFI API去取得系統目前的記憶體對映。
  4. OS會有一些儲存Boot Path的選項以及Boot Option, 用環境變數的形式存在非揮發的儲存裝置(nonvolatile storage)裡
  5. 呼叫ExitBootServices(),這個呼叫可以由OS Loader或試OS Kernel來完成, 要特別注意的是必須要在呼叫這個Function之前,要取得目前記憶體對映(Current Memory Map)。
  6. 一旦呼叫了ExitBootServices(),就再也沒有EFI Boot Service可以使用了,只能使用EFI Runtime Service。

 OS Loader的範例程式呼叫了幾種EFI Library來做一些簡單的實作. 
  1. Print出OS Loader自己的Device Path以及File Path. 
  2. Print出OS Loader存在的記憶體位址,,以及使用了多少Bytes
  3. OS Loader 載入OSKERNEL.BIN到Memory, 而OSKERNEL.BIN是從OS Loader相同的目錄中取得.
  4. Print數個Block Device的第一個Block。 第一個Block是Floppy Drive的FAT12 File System. 第二個是HDD的MBR(Master Boot Record)。 第三個則是相同HDD的大FAT32分割區。 第四個是相同HDD的小FAT16分割區。
  5. Print所有System Configuration Table, 系統目前記憶體對映的指標以及列出所有系統的環境變數  
  6. 最後OS Loader呼叫ExitBootServices()。


1.Print出OS Loader自己的Device Path以及File Path。
2.Print出OS Loader存在的記憶體位址,,以及使用了多少Bytes


  1. 從ImageHandle取得LOADED_IMAGE_PROTOCOL interface放進LoadedImage
  2. 再從DeviceHandle取得DEVICE_PATH_PROTOCOL interface存到 DevicePath
後面就將找到的DevicePath & FilePath &ImageBase及ImageSize印出來

BS->HandleProtocol(
             ImageHandle,
             &LoadedImageProtocol,
             LoadedImage);

BS->HandleProtocol(
             LoadedImage->DeviceHandle,
             &DevicePathProtocol,
             &DevicePath);

Print (
   L"Image device : %s\n",
   DevicePathToStr (DevicePath));

Print (
   L"Image file : %s\n",
   DevicePathToStr (LoadedImage->FilePath));

Print (
   L"Image Base : %X\n",
   LoadedImage->ImageBase);

Print (
   L"Image Size : %X\n",
   LoadedImage->ImageSize);


3.OS Loader 載入OSKERNEL.BIN到Memory, 而OSKERNEL.BIN是從OS Loader相同的目錄中取得。



  1. 從DeviceHandle取得FILE_SYSTEM_PROTOCOL interface放進Vol
  2. 再透過Vol的OpenVolume取得跟OS Loader一樣Partition位置的file handle(CurDir)。


BS->HandleProtocol(
             LoadedImage->DeviceHandle,
             &FileSystemProtocol,
             &Vol);

Vol->OpenVolume(
         Vol,
         &RootFs);

CurDir = RootFs;

      3.Build OSKERNEL.BIN的File PATH,這個OSKERNEL.BIN跟OS Loader Image存放在一樣的Directory,將OSKERNEL.BIN存進OsKernelBuffer

StrCpy(FileName,DevicePathToStr(LoadedImage->FilePath));
for(i=StrLen(FileName);i>=0 && FileName[i] != '\\';i--);

FileName[i] = 0;

StrCat(FileName,L"\\OSKERNEL.BIN");
      CurDir->Open (CurDir, &FileHandle,FileName,
      EFI_FILE_MODE_READ,0);
Size = 0x00100000;
BS->AllocatePool(EfiloaderData,Size,&OsKernelBuffer);

FileHandle->Read(FileHandle,&Size,OsKernelBuffer);
FileHandle->Close(FileHandle);
4.Print數個Block Device的第一個Block


  1. 使用LibLocateHandle找到Block Io Device的List 後存在 HandleBuffer
  2. BlkIo->ReadBlocks 將找到的第一個Block讀取出來

NoHandles = 0;

HandleBuffer = NULL;

LibLocateHandle (ByProtocol, &BlockIoProtocol,NULL,
&NoHandles, &HandleBuffer);

for(i=0;iHandleProtocol(
          HandleBuffer[i],
          &DevicePathProtocol,
          &DevicePath
    );

    BS->HandleProtocol(
          HandleBuffer[i],
          &BlockIoProtocol,
          &BlkIo
    );

    Block = AllocatePool (BlkIo->BlockSize);

    MediaId = BlkIo->MediaId;

    BlkIo->ReadBlocks(
            BlkIo,
            MediaId,
            (EFI_LAB)0.
            BlkIo->BlockSize,
            Block
            );

     Print(
       L"\nBlock #0 of device
      %s\n",DevicePathToStr(DevicePath));

     DumpHex(0,0,BlkIo->BlockSize,Block);
    
}
5.Print所有System Configuration Table, 系統目前記憶體對映的指標以及列出所有系統的環境變數  


1.在下面會列出所有的System Configuration Table

LibGetSystemConfigurationTable(
              &AcpiTableGuid,&AcpiTable
              );

LibGetSystemConfigurationTable(
              &SMBIOSTableGuid,&SMBIOSTable
              );

LibGetSystemConfigurationTable(
              &SalSystemTableGuid,&SalSystemTable
              );

LibGetSystemConfigurationTable(
              &MpsTableGuid,&MpsTable
              );


Print(
 L" ACPI Table is ad address
        %X\n",AcpiTable
);

Print(
 L" SMBIOS Table is ad address
        %X\n",SMBIOSTable
);

Print(
 L" AcPI Table is ad address
        %X\n",SalSystemTable
);

Print(
 L" MPS Table is ad address
        %X\n",MpsTable
);



2.在下面會列出所有記憶體對映的指標

MemoryMap = LibMemoryMap(
            &NoEntries,
            &MapKey,
            &DescriptorSize,
            &DescriptorVersion,
            );

Print(
   L"Memory Descriptor List:\n\n"
);

Print(
   L"Type Start Address End Address Attributes \n"
);

Print(
   L"============== ============== ==============\n"
);

MemoryMapEntry = MemoryMap;

for(i=0;iType],
      MemoryMapEntry->PhysicalStart,
      MemoryMapEntry->PhysicalStart +
          LShiftU64(
                MemoryMapEntry->NumberOfPages,
                PAGE_SHIFT)-1,
                MemoryMapEntry->Attribute
         );

   MemoryMapEntry = NextMemoryDescriptor(
                          MemoryMapEntry.
                          DescriptorSize
                          );
      
}

3.列出所有系統的環境變數

VariableName[0] = 0x0000;

VendorGuid = NullGuid;

Print(
   L"GUID
    Value\n");

Print(
   L"=======================================
    ===================\n");

do{
   VariableNameSize = 256;
   Status = RT->GetNextVariableName(
                &VariableNameSize,
                VariableName,
                &VendorGuid
             );

   if (Status == EFI_SUCCESS) {
      VariableValue = LibGetVariable(
                        VariableName,
                        &VendorGuid
                       );

    Print(
        L"%.-35g %.-20s
         %X\n",&VendorGuid,VariableName,VariableValue
     );
   }
}while (Status == EFI_SUCESS);


留言