Obecnie studiuję niskopoziomową organizację systemów operacyjnych. Aby to osiągnąć, staram się zrozumieć, jak ładowane jest jądro Linuksa.Przejście z trybu rzeczywistego do chronionego w jądrze Linuksa
Rzecz, której nie mogę zrozumieć, to przejście z 16-bitowego (tryb rzeczywisty) do 32-bitowego (tryb chroniony). Zdarza się to w this file.
Funkcja protected_mode_jump
wykonuje różne obliczenia pomocnicze dla 32-bitowego kodu, który jest wykonywany później, a następnie umożliwia PE
bit w CR0
reguster
movl %cr0, %edx
orb $X86_CR0_PE, %dl # Protected mode
movl %edx, %cr0
a potem wykonuje długi skok do kodu 32-bitowego:
# Transition to 32-bit mode
.byte 0x66, 0xea # ljmpl opcode
2: .long in_pm32 # offset
.word __BOOT_CS # segment
o ile mi zrozumieć in_pm32
jest adres funkcji 32-bitowym, który oświadczył tuż poniżej protected_mode_jump
:
.code32
.section ".text32","ax"
GLOBAL(in_pm32)
# some code
# ...
# some code
ENDPROC(in_pm32)
Podstawa __BOOT_CS
sektor 0 (GDT jest ustawiony wcześniej here), więc to oznacza, że przesunięcie powinno być w zasadzie absolutny adres funkcji in_pm32
.
To jest problem. Podczas generowania kodu maszynowego asembler/linker nie powinien znać adresu bezwzględnego funkcji in_pm32
, ponieważ nie wie, gdzie będzie załadowany do pamięci w trybie rzeczywistym (różne programy ładujące mogą zajmować różne ilości miejsca, a tryb rzeczywisty jądro jest ładowane tuż po bootloaderze).
Ponadto skrypt linkera (setup.ld
w tym samym folderze) określa pochodzenie kodu jako 0, więc wygląda na to, że adres będzie przesunięciem od początku jądra trybu rzeczywistego. Powinien działać dobrze z 16-bitowym kodem, ponieważ rejestr CS
jest ustawiony prawidłowo, ale kiedy dojdzie do skoku w dal, procesor jest już w trybie chronionym, więc względne przesunięcie nie powinno działać.
Moje pytanie: Dlaczego skok w dal w trybie chronionym (.byte 0x66, 0xea
) określa prawidłową pozycję kodu, jeśli przesunięcie (.long in_pm32
) jest względne?
Wygląda na to, że brakuje mi czegoś naprawdę ważnego.
Już idę do łóżka, jak to jest 3:30 rano. Widziałem tylko część dotyczącą JMP. Ten skok jest 32-bitowym FAR JMP, który ustawia selektor _CS_. Ustawienie bitów trybu chronionego w CR0 powoduje przejście do quasi-16-bitowego trybu chronionego. Aby przejść do trybu chronionego 32-bitowym, musisz wykonać FAR JMP, który przyjmuje selektor (__BOOT_CS) i offset i przeskakuje do niego. __BOOT_CS powinien być selektorem, który wskazuje na 32-bitowy deskryptor segmentu kodu (prawdopodobnie z podstawą 0 i limitem 0xffffffff) w GDT. Po zakończeniu FAR JMP będzie on w trybie chronionym 32-bitowym. –
Zasadniczo FAR JMP jest wymagany, aby przejść z trybu quasi 16-bitowego do trybu chronionego 32-bitowo. –
Rzeczywiście, podstawa selektora '__BOOT_CS' wynosi 0. Przypuszczam, że oznacza to, że FAR JUMP powinien przyjmować adres bezwzględny żądanej funkcji/etykiety (ponieważ' 0 + offset' będzie po prostu 'offsetem '). Ale FAR JUMP jest tutaj wywoływany z względnym przesunięciem ('.long in_pm32' jest adresem funkcji' in_mp32' od początku na binarnym jądrze trybu rzeczywistego) - i nie rozumiem, dlaczego na końcu funkcja 'in_pm32' jest wykonywany. Skok daleki powinien być niedopasowany na bajtach 0x7C00 + bootloader_size. – Alexander