簡體   English   中英

在 C/C++ 中退出引導服務后的 GPT 文件系統

[英]GPT FileSystem after exiting boot services in C/C++

我正在創建一個操作系統,並且在過去的幾個月里一直在努力尋找一種在其中實現文件系統的方法。 我可以在引導服務中讀取文件,但在 UEFI 中退出引導服務后,function 無法讀取該文件。 我正在嘗試使用 GPT 在引導服務之外創建一個文件系統驅動程序(盡管可能是 MBR,但基於我所看到的幾乎不可能)。 我見過的所有源代碼和示例都使用帶有 GRUB 的多重引導,但我不使用 GRUB 引導加載程序。 相反,我遵循了 Poncho 的 OSDev 2 系列中的那個。 我從 WYOOS 看到了一個示例,他使用 MSDos 分區系統,但它依賴於多重引導,因此它在我的場景中不起作用。 所有幫助將不勝感激。 謝謝。

您在問題中沒有提供太多信息,但我假設您想要一個能夠從文件系統上的驅動器讀取文件的硬盤驅動器。 最后,您並沒有真正實現文件系統驅動程序。 您實現了一個能夠讀取/寫入硬盤驅動器的硬盤驅動器驅動程序。 文件系統邏輯位於該驅動程序之上。

在此之下,您需要一個 ACPI 解釋器和一個用於管理 PCI 設備的 PCI 子系統。 這真的很復雜,不像調用 function 來完成工作。 一旦你得到了一個 PCI 子系統、一個 ACPI 解釋器和一個文件系統驅動程序,你就得到了一個相當先進的操作系統。 這不會是一個 function 調用。 它需要規划和閱讀規范。

話雖如此,我目前正在自己編寫一個 x86-64 操作系統,我打算做的事情可能會感興趣(或不感興趣)。 從 UEFI,我使用以下命令從磁盤加載 kernel:

    EFI_GUID FSPGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
    EFI_HANDLE* Handles = NULL;   
    UINTN HandleCount = 0;
    Status = BS->LocateHandleBuffer(ByProtocol, &FSPGuid, NULL, &HandleCount, &Handles);
    if (EFI_ERROR(Status)){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Could not locate the handle buffer.\n");
        goto DONE;
    }else{
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Located the handle buffer for file system protocol.\n");
    }
    
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* FS = NULL; 
    EFI_FILE_PROTOCOL* Root = NULL;
    EFI_FILE_PROTOCOL* Token = NULL;
    for (UINTN index = 0; index < (UINTN)HandleCount; index++)
    {
            Status = BS->HandleProtocol(Handles[index], &FSPGuid, (void**)&FS);
        Status = FS->OpenVolume(FS, &Root);
        Status = Root->Open(Root, &Token, L"startup.elf", EFI_FILE_MODE_READ, EFI_FILE_MODE_WRITE);
        if(!EFI_ERROR(Status))
            break;
    }
    if (Token == NULL){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Could not locate startup file.\n");
        goto DONE;  
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Located the startup file.\n");
    UINTN BufferSize = 50000;
    CHAR8 StartupBuffer[50000];
    Status = Token->Read(Token, &BufferSize, StartupBuffer);
    if(EFI_ERROR(Status)){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Located startup.elf, but could not read from it.\n");
        goto DONE;
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Could read startup.elf properly now jumping to it's entry point.\n");
        
    
    Status = Root->Open(Root, &Token, L"kernel.elf", EFI_FILE_MODE_READ, EFI_FILE_MODE_WRITE);
    if(EFI_ERROR(Status)){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Could not locate kernel file.\n");
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Could locate the kernel file.\n");
    if (Token == NULL){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Could not locate kernel file.\n");
        goto DONE;  
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Located the kernel file.\n");
    EFI_PHYSICAL_ADDRESS PhysicalBuffer;
    UINT64* KernelBuffer;
    Status = BS->AllocatePages(AllocateAnyPages, EfiBootServicesData, 4096, &PhysicalBuffer);
    KernelBuffer = (UINT64*)(UINTN)PhysicalBuffer;
    if(EFI_ERROR(Status)){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Could not allocate pages for the kernel.\n");
        goto DONE;
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Allocated pages for the kernel\n");
    BufferSize = 4096 * 4096;
    Status = Token->Read(Token, &BufferSize, KernelBuffer);
    if(EFI_ERROR(Status)){
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Kernel Panic!!: Located kernel.elf, but could not read from it.\n");
        goto DONE;
    }else
        SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Could read kernel.elf properly.\n");

我加載了一個名為 startup.elf 的文件,它將為 kernel 設置虛擬 memory 的高半映射。 然后我為比這小得多的 kernel 分配了很多頁面(4096)。 然后,startup.elf 可執行文件從固定地址獲取 kernel 的第一個字節的地址,並將內核的可加載段從物理 memory 中的 0x400000 開始映射。 因此,虛擬地址空間的高 2GB 從 0x0 映射到物理 memory 中的 2GB。 因此,內核在虛擬 memory 中的位置是 0xffff_ffff_8040_0000。 這是我從 startup.elf 文件跳轉到的地方。 可能有幾種方法可以做到這一點,但我更喜歡為我的 kernel (啟動可執行文件和內核的可執行文件)設置 2 個單獨的文件。

從那里開始,我打算實現一個 memory 管理子系統。 Memory 管理是您要實施的第一件事,因為所有其他子系統都在使用 memory。 您希望能夠為內核的需要分配 memory。

之后,我計划實現一個 ACPI 子系統,該子系統將解析所有 ACPI 表並解釋 AML 語言,該語言將允許收集有關我需要驅動的主板上存在的信息的信息。 為此,我從 UEFI 獲取 RSDP,並從 RSDP 中找到其他表(參見https://wiki.osdev.org/RSDP )。 ACPI 表允許收集有關計算機上硬件的所有信息。 例如,MCFG 告訴您 PCI 設備的配置空間將從哪里開始(參見https://wiki.osdev.org/PCI )。

在 ACPI 之上,我計划實現一個 PCI 子系統,它允許某種通用接口讀取和寫入 PCI 寄存器和某種通用中斷號分配器。 PCI 目前主要與 MSI/MSI-X 配合使用。 我打算忘記所有遺留下來的東西,只使用最現代的東西,並假設所有這些硬件的存在(今天 99% 的台式 x64 計算機上都存在)。 Windows 對他們最近的要求也是如此。 他們只是給出了運行操作系統的一些基本要求,不支持 rest。 這與 Linux kernel 不同,后者試圖支持所有東西,直到非常古老的恐龍計算機。 這使得代碼非常臃腫並且大部分時間都不是很有用。

AHCI驅動程序將使用 PCI 子系統來管理硬盤。 AHCI 是使用 MSI 觸發中斷的 PCI 設備(請參閱https://www.intel.ca/content/www/ca/en/io/serial-ata/serial-ata-ahci-spec-rev1-3-1 .html )。 UEFI 固件附帶 AHCI 驅動程序。 這就是它設法寫入/讀取硬盤驅動器的原因。

您會發現,在真正開始從硬盤驅動器加載內容之前,必須完成很多工作。 您需要編寫 memory 管理方案,解釋 AML 和 ACPI 表,解析 PCI 總線以查找設備及其配置空間,最后編寫 AHCI 驅動程序。 即便如此,您可能還沒有完成,因為現代 SATA SSD 具有非常高的吞吐量。 因此,它們一次在幾個 MB 中加載和存儲數據。 您需要某種有效的算法來緩存硬盤的某些部分,並確保不會一直加載/存儲,因為它需要大量的 RAM 空間。 對於較舊的硬盤,它會更容易但並不容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM