2012-02-04 31 views
10

Obecnie gram z x86 Assember, aby wyostrzyć moje umiejętności programowania niskiego poziomu. Obecnie mam mały problem ze schematem adresowania w 32-bitowym trybie chronionym.Skok asemblera w trybie chronionym z GDT

Sytuacja jest następująca:

Mam program załadowany w 0x7e0 który przełącza procesor do trybu chronionego i przeskakuje do według etykiety w kodzie:

[...] 
code to switch CPU in Protected Mode 
[...] 

jmp ProtectedMode 


[...] 

bits 32 

ProtectedMode: 
    .halt: 
     hlt 
     jmp .halt 

ten działa całkowicie w porządku tak daleko. "Jmp ProtectedMode" działa bez wyraźnego dalekiego przeskoku, aby usunąć kolejkę wstępnego pobierania - ponieważ program ten jest załadowany z przesunięciem 0 (org 0 na początku) - powodując segment kodu wskazujący właściwą lokalizację.

Mój obecny problem polega na tym, że w etykiecie "ProtectedMode" chcę przejść do innego programu, który jest ładowany pod 0x8000 (sprawdziłem to za pomocą zrzutu pamięci, funkcja ładowania działała poprawnie i program został załadowany poprawnie do 0x8000).

Ponieważ procesor jest już w trybie ProtectedMode, a nie RealMode, schemat adresowania jest inny. ProtectedMode używa selektorów deskryptorów do wyszukiwania adresu bazowego i limitu w tabeli deskryptorów, aby dodać dane przesunięcie i pobrać adres fizyczny (jak rozumiem). Dlatego konieczne było zainstalowanie GDT przed wejściem do ProtectedMode.

Kopalnia wygląda tak:

%ifndef __GDT_INC_INCLUDED__ 
%define __GDT_INC_INCLUDED__ 

;********************************* 
;* Global Descriptor Table (GDT) * 
;********************************* 
NULL_DESC: 
    dd 0   ; null descriptor 
    dd 0 

CODE_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10011010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

DATA_DESC: 
    dw 0xFFFF  ; data descriptor 
    dw 0   ; limit low 
    db 0   ; base low 
    db 10010010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

gdtr: 
    Limit dw 24   ; length of GDT 
    Base dd NULL_DESC ; base of GDT 

%endif ;__GDT_INC_INCLUDED__ 

i jest ładowany do GDT zarejestrować poprzez

lgdt [gdtr] 

Co nie rozumiałem tak daleko, jak mogę teraz przejść do fizycznego adres 0x8000 w ProtectedMode za pomocą GDT?

Moje pierwsze myśli polegały na wybraniu deskryptora kodu (CODE_DESC), który powinien wskazywać na 0x7e00 (jeśli aktualny program został załadowany) i użyć przesunięcia, które jest konieczne, aby uzyskać 0x8000 (512 bajtów), co skutkuje instrukcją skoku :

jmp CODE_DESC:0x200 

Ale to nie działa.

jmp 0x7e0:0x200 

nie działa albo ...

Czy masz jakiś pomysł, co ja tu brakuje? Może nie zrozumiałem czegoś istotnego w ramach 32-bitowego schematu adresowania ProtectedMode i użycia GDT.

[EDIT] Kompletny kod:

bits 16 
org 0      ; loaded with offset 0000 (phys addr: 0x7e00) 

jmp Start 

Start: 
    xor ax, ax 
    mov ax, cs 
    mov ds, ax    ; update data segment 

    cli      ; clear interrupts 

    lgdt [gdtr]    ; load GDT from GDTR (see gdt_32.inc) 

    call OpenA20Gate  ; open the A20 gate 

    call EnablePMode  ; jumps to ProtectedMode 

;****************** 
;* Opens A20 Gate * 
;****************** 
OpenA20Gate: 
    in al, 0x93   ; switch A20 gate via fast A20 port 92 

    or al, 2   ; set A20 Gate bit 1 
    and al, ~1   ; clear INIT_NOW bit 
    out 0x92, al 

    ret 

;************************** 
;* Enables Protected Mode * 
;************************** 
EnablePMode: 
    mov eax, cr0 
    or eax, 1 
    mov cr0, eax 

    jmp ProtectedMode ; this works (jumps to label and halts) 
    ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work 
    ;jmp 08h:ProtectedMode , => does not work 

;*************** 
;* data fields * 
;* &includes * 
;*************** 
%include "gdt_32.inc" 

;****************** 
;* Protected Mode * 
;****************** 
bits 32 

ProtectedMode: 
    ;here I want to jump to physical addr 0x8000 (elf64 asm program) 

    .halt: 
     hlt 
     jmp .halt 

Odpowiedz

11

Istnieje wiele problemów w kodzie.

pierwszy, GDTR.Base zawiera przesunięcie GDT od początku kodu, ponieważ kod jest kompilowany, aby rozpocząć pod adresem 0 (z powodu org 0). Podstawowy adres musi być adresem fizycznym, a nie adresem względnym. IOW, jeśli zachowujesz to org 0, musisz dodać CS * 16 (= 0x7e00) do Base.

Po drugie, z powodu tej samej org 0, że 32-bitowe offsety w kodzie (po bits 32 i ProtectedMode:) nie są równe adresów fizycznych oni odpowiadają, że są 0x7e00 poniżej adresów fizycznych. OTOH, segmenty zdefiniowane w GDT zaczynają się od adresu fizycznego 0 (ponieważ podstawowe części wpisów GDT to 0), a nie 0x7e00. Oznacza to, że przy próbie użycia tych segmentów z kodem/danymi, będzie brakować adresów przez 0x7e00. Jeśli chcesz zachować org 0, adresy bazowe w GDT muszą mieć wartość 0x7e00.

Lub możesz zmienić org 0 na org 0x7e00, a następnie baz w GDT powinno być 0. I nie będziesz musiał dostosować GDTR.Base przez 0x7e00, 0 zrobi.

To powinno działać:

bits 16 
org 0x7e00     ; loaded at phys addr 0x7e00 
          ; control must be transferred with jmp 0:0x7e00 

    xor ax, ax 
    mov ds, ax    ; update data segment 

    cli      ; clear interrupts 

    lgdt [gdtr]    ; load GDT from GDTR (see gdt_32.inc) 

    call OpenA20Gate  ; open the A20 gate 

    call EnablePMode  ; jumps to ProtectedMode 

;****************** 
;* Opens A20 Gate * 
;****************** 
OpenA20Gate: 
    in al, 0x93   ; switch A20 gate via fast A20 port 92 

    or al, 2   ; set A20 Gate bit 1 
    and al, ~1   ; clear INIT_NOW bit 
    out 0x92, al 

    ret 

;************************** 
;* Enables Protected Mode * 
;************************** 
EnablePMode: 
    mov eax, cr0 
    or eax, 1 
    mov cr0, eax 

    jmp (CODE_DESC - NULL_DESC) : ProtectedMode 

;*************** 
;* data fields * 
;* &includes * 
;*************** 
;%include "gdt_32.inc" 
;********************************* 
;* Global Descriptor Table (GDT) * 
;********************************* 
NULL_DESC: 
    dd 0   ; null descriptor 
    dd 0 

CODE_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10011010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

DATA_DESC: 
    dw 0xFFFF  ; limit low 
    dw 0   ; base low 
    db 0   ; base middle 
    db 10010010b ; access 
    db 11001111b ; granularity 
    db 0   ; base high 

gdtr: 
    Limit dw gdtr - NULL_DESC - 1 ; length of GDT 
    Base dd NULL_DESC ; base of GDT 

;****************** 
;* Protected Mode * 
;****************** 
bits 32 

ProtectedMode: 
    mov  ax, DATA_DESC - NULL_DESC 
    mov  ds, ax ; update data segment 

    .halt: 
     hlt 
     jmp .halt 

Zauważ, że limit segmentu jest równa wielkości segmentu minus 1.

kilka więcej punktów ... Załaduj wszystkie rejestry segmentowe z obowiązującymi selektorów lub 0. Ponadto, ustaw stos. Jeśli masz tam śmieci (lub stare wartości z trybu rzeczywistego), gdy zaczniesz grać z przerwaniami/wyjątkami, będziesz miał więcej awarii.

W końcu nie wiem, czym jest elf64, ale trzeba będzie zadbać o to, co jest w przypadku innych modułów i upewnić się, że wszystkie wygenerowane adresy odpowiadają adresom ładowania. A jeśli zamierzasz włączyć tryb 64-bitowy, musisz wykonać mnóstwo pracy. Radzę nie spieszyć się z trybem 64-bitowym, mimo że potykasz się o względnie proste rzeczy.

+0

Dziękuję za wyjaśnienie ... Właśnie dlatego robię to - i to nie jest takie proste, kiedy robisz to od podstaw po raz pierwszy, czytając referencje x86 :)! Btw: Czy jest jakaś świetna książka, którą możesz doradzić, która zajmuje się właśnie takimi tematami? –

+1

Nie znam dobrych książek. Oficjalna dokumentacja od firmy Intel i AMD zawiera wszystkie informacje, nie jest to typowy rodzaj książki lub podręcznika, który można przeczytać od razu i zrozumieć wszystko (przy okazji, istnieje wiele literówek i okazjonalnych błędów w dokumentach Intela). Istnieje wiele artykułów i samouczków online. I zawsze możesz eksperymentować. Lub zobacz czyjś kod i zadawaj pytania. Zobacz te grupy: [alt.os.development] (http://groups.google.com/group/alt.os.development/topics), [comp.lang.asm.x86] (http://groups.google .com/group/comp.lang.asm.x86/topics). –

+0

Dzięki za porady !! Popatrzę na to! –

3

Kilka rzeczy. Po pierwsze, twój obecny kod nie wchodzi technicznie w tryb chroniony. Wchodzisz w tryb chroniony, ładując cs z deskryptorem z GDT. Ponieważ nie można bezpośrednio ustawić rejestru cs, najprostszym sposobem na to jest użycie dalekiego skoku. Zastąp swój obecny skok przez:

jmp (CODE_DESC-NULL_DESC):ProtectedMode 

Po drugie, podstawą dla segmentu kodu jest 0, a nie 0x7e00.Jeśli spojrzysz na cztery bajty oznaczone słowem "base", wszystkie są 0. Masz dwie opcje. Zmień bazę GDT, tak aby miała bazę 0x7e00, lub dodaj dyrektywy, aby zmienić adres ładowania dla wszystkich kodów trybu chronionego dla podstawy 0.

Po wykonaniu obu tych czynności można przejść do programu za pomocą normalna instrukcja skoku. Jeśli zdecydujesz się opuścić GDT jak to jest, należy użyć pełnego adresu:

jmp 0x8000 

Jeśli zdecydujesz się zmienić podstawę swojego segmentu kodu, trzeba będzie używać adresów w stosunku do uzyskanego.

jmp 0x200 

More information about the GDT
More information about entering protected mode

+0

Dziękuję za odpowiedź .. OK - podczas korzystania z instrukcji skoku "jmp CODE_DESC: ProtectedMode" potrójne awarie CPU i resetuje (ponieważ ten kierunek skoku wydaje się skakać gdzieś). "jmp ProtectedMode" przeskakuje do właściwej etykiety i zatrzymuje system. Ponieważ może to być związane z problemem bazy GDT, zmienię GDT i spróbuję ponownie. Dziękuję za szybką odpowiedź !! –

+0

@ughoavgfhw Czy masz na myśli 'jmp (CODE_DESC-NULL_DESC): ProtectedMode'? – Nayuki

+0

@NayukiMinase Dzięki za złapanie tego, założyłem, że są już offsety. – ughoavgfhw