作為核心的記憶體布局來源,BIOS提供了兩套記憶體布局資訊,第一個是legacy啟動時提供的e820 memory map,另一個是
efi啟動時提供的efi memory map。
在繼續之前,我們需要知道,x86啟動的順序是,BIOS,grub,real mode,protect mode
其中,real mode是進入核心前的最後一步,在這個模式下,會根據系統是legacy還是efi啟動,
擷取到不同的memory map資訊。
首先對於legacy模式,有可能是grub,也可能是real mode代碼,根據BIOS提供的介面,擷取
到記憶體資訊,儲存到boot_params結構體。(見Zero-page.txt)
real mode擷取記憶體布局的代碼在:
arch\x86\boot/memory.c的detect_memory_e820函數,用BIOS提供的int service,迴圈執行
intcall(0x15, &ireg, &oreg);,擷取所有的e820 map資訊,儲存到boot_params.e820_map。
對於efi啟動方式,realmode下,則可能是arch/x86/boot/compressed/eboot.c裡,
exit_boot時,調用efi提供的system table->get_memory_map,擷取到記憶體布局資訊,
再賦值給boot_params.efi_info。
進入protect模式後,
void __init setup_arch(char **cmdline_p){ setup_memory_map();if (efi_enabled(EFI_BOOT))efi_init();}
有了boot_params後,相關的記憶體布局初始化setup_memory_map:
該函數通過default_machine_specific_memory_setup,
先在sanitize_e820_map函數裡,把boot_params.e820_map去重排序,再通過
append_e820_map加到全域變數資料e820_map裡。接著e820_print_map
會列印這個e820 map的記憶體布局,也就是我們看到的:
[ 0.000000] e820: BIOS-provided physical RAM map:[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x0000000000057fff] usable[ 0.000000] BIOS-e820: [mem 0x0000000000058000-0x0000000000058fff] reserved[ 0.000000] BIOS-e820: [mem 0x0000000000059000-0x000000000009dfff] usable
注意,以上只對legacy有效,因為他檢查的是e820_map,對於efi模式,e820_map是null 指標。
再看efi記憶體布局初始化,還是在setup_arch裡:
if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature, EFI32_LOADER_SIGNATURE, 4)) {set_bit(EFI_BOOT, &efi.flags);} else if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature, EFI64_LOADER_SIGNATURE, 4)) {set_bit(EFI_BOOT, &efi.flags);set_bit(EFI_64BIT, &efi.flags);}if (efi_enabled(EFI_BOOT))efi_memblock_x86_reserve_range();if (efi_enabled(EFI_BOOT))efi_init();
如果boot_params裡有efi標誌,那麼就根據boot_params.efi_memmap擷取efi的分區資訊,
最後,還是通過e820_add_region加入到e820 table裡去。具體流程是:
efi_init -> efi_memmap_init -> do_add_efi_memmap -> e820_add_region
To be more specific, I found a hibernation error which complains of:
A very good summary of efi linux at:
https://lwn.net/Articles/632528/