使用RUST編寫OS(2)-最小內核

學習RUST同時學習OS


引導啟動

當我們啟動電腦時,主板內的ROW儲存的固件(firmware)將會運行,他將會負責電腦的加電自檢(power-on self test)和可用內存的檢測(available ram) 以及CPU和其他硬件的預加載,之後將尋找一個可引導的儲存介質(bootable disk),並開始啟動內核(kernel)


x86架構支持兩種固件標準:BIOS和UEFI,本系列將會以BIOS為標準

BIOS 啟動

當電腦啟動時

  1. 主板上特殊的閃存中儲存的BIOS將會被加載
  2. BIOS將會加電自檢,初始化硬件,然後尋找可引導的儲存介質
  3. 當找到後,電腦控制全將轉移給引導程序(bootloader)
    • 一段儲存在存儲介質的開頭,512byte的程序片段,大多數引導程序都不會超過512byte,所以通常情況下都會分為
      1. 優先啟動,長度不超過512byte,儲存在介質開頭的第一階段引導程序(first stage bootloader)
      2. 隨後加載,長度較第一段長,儲存在其他位置的第二階段引導程序(second stage bootloader)

引導程序的作用

  1. 引導程序先決定內核位址,並將內核加載到內存
  2. 引導程序將CPU從16位元的實模式,切換到32位元的保護模式(protected mod),最終再切換到64位元的長模式(long mod),此時才能訪問所有64位元的內存器和主位元
  3. 引導程序能從BIOS查詢特定訊息,並轉傳到內核,如查詢和傳遞內存映射表 (memory map)

使用Assembly編寫一個簡單的BIOS

  • 將會在畫面上顯示A到Z
mov ah, 0x0e
mov al, 65
int 0x10
 
loop:
inc al
cmp al, 'Z' + 1
je exit
int 0x10
jmp loop
 
exit:
jmp $
times 510-($-$$) db 0 #內存啟動位址
db 0x55, 0xaa
 

Multiboot 標準

FieldTypeValue
magic numberu320xE85250D6
architectureu320 for i386, 4 for MIPS
header lengthu32total header size, including tags
checksumu32-(magic + architecture + header_length)
tagsvariable
end tag(u16, u16, u32)(0, 0, 8)
section .multiboot_header
header_start:
    dd 0xe85250d6                ; magic number (multiboot 2)
    dd 0                         ; architecture 0 (protected mode i386)
    dd header_end - header_start ; header length
    ; checksum
    dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))
 
    ; insert optional multiboot tags here
 
    ; required end tag
    dw 0    ; type
    dw 0    ; flags
    dd 8    ; size
header_end:
  • 0xe85250d6 為 multiboot 的 magic number
  • dd 為 defined double 32 bit
  • dw 為 defined word 16 bit
  • 0x100000000 為特殊記憶體位置,可以在compiler的時候避免報錯

最小內核

安裝Rust Nightly

  1. stable
  2. beta
  3. nightly

安裝方法

  1. 使用rustup
    rustup override add nightly
  2. 新增名為rust-toolchain的文件且內容為nightly

目標配置清單

  • 使用目標配置清單是為了讓我們的系統不依賴底層配置系統
  • 編寫目標系統可以使用json格式完成
{
  "llvm-target": "x86_64-unknown-none",
  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
  "arch": "x86_64",
  "target-endian": "little",
  "target-pointer-width": "64",
  "target-c-int-width": "32",
  "os": "none",
  "executables": true
}
  • 因為要在裸機上運行,因此llvm-target為none

添加額外配置

  • 不使用平台默認連接器,使用跨平台LLD鏈接器
{
  "linker-flavor": "ld.lld",
  "linker": "rust-lld"
}
禁用紅區
  • 紅區為一種優化方式,當有n個變量時,將會調整指針到一個合適的地方,來確保返回值和局部變量有足夠空間。不過CPU異常處理機制會覆蓋紅區,但被中斷的函數卻引用這些數據,因此從中斷恢復後反而會引發更大錯誤
  • 為了處理中斷,我們需要禁用紅區(redzone),避免榐指針優化進而破壞榐
{
  "disable-redzone": true
}
禁用SIMD
  • 操作系統在處理硬件中斷時,需要保存所有寄存器信息到內存中,在中斷結束後再將其恢復以供使用。所以說,如果內核需要使用SIMD寄存器,那麼每次處理中斷需要備份非常多的數據(512-1600字節),這會顯著地降低性能。要避免這部分性能損失
  • 對內核禁用SIMD,提高性能表現(不代表內核不支援SIMD)
{
  "features": "-mmx,-sse,+soft-float"
}

完整目標配置清單

{
    "llvm-target": "x86_64-unknown-none",
    "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
    "arch": "x86_64",
    "target-endian": "little",
    "target-pointer-width": "64",
    "target-c-int-width": "32",
    "os": "none",
    "executables": true,
    "linker-flavor": "ld.lld",
    "linker": "rust-lld",
    "panic-strategy": "abort",
    "disable-redzone": true,
    "features": "-mmx,-sse,+soft-float"
}