2012-02-04 9 views
10

Şu anda düşük seviyeli programlama becerilerimi keskinleştirmek için x86 Assember ile uğraşıyorum. Şu anda, 32-Bit Korumalı Modda adresleme şeması ile küçük bir sorunla karşı karşıya.Assembler, Korunan Modda GDT ile atla

durum şudur:

Ben Korumalı Mod ile CPU geçer ve kodda göre etikete atlar 0x7e0 yüklenen bir Programı var:

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

jmp ProtectedMode 


[...] 

bits 32 

ProtectedMode: 
    .halt: 
     hlt 
     jmp .halt 

Bu kesinlikle iyi böylece çalışır uzak. "Jmp ProtectedMode", ön yükleme kuyruğunu temizlemek için açık bir atlama olmadan çalışır - bu program, ofset 0 (başlangıçta orj 0) ile yüklendiğinden, kod parçasının doğru konuma işaret etmesine neden olur.

Şu anki sorunum şu ki, "ProtectedMode" etiketi içinde 0x8000'de yüklü olan başka bir programa atlamak istiyorum (bunu bir bellek dökümü ile kontrol ettim, yükleme işlevi düzgün çalışıyordu ve program yüklendi doğru 0x8000). İşlemci ProtectedMode şimdi ve artık realmode değil beri

, adresleme şeması farklıdır. ProtectedMode, belirli bir ofset eklemek ve fiziksel adresi (anladığım gibi) almak için bir temel adres ve bir tanımlayıcı tablosundaki bir sınırı aramak için tanımlayıcı seçicilerini kullanır. Bu nedenle, ProtectedMode girmeden önce bir GDT'nin kurulması gerekliydi. Şimdiye kadar is anlamadı ne

lgdt [gdtr] 

, nasıl şimdi fiziksel atlamak yok yoluyla kayıt

%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__ 

ve GDT için yüklenir:

Mayın aşağıdaki gibi bakıyor GDT'yi kullanarak ProtectedMode'da adres 0x8000?

İlk düşüncelerim 0x7e00 (geçerli program yüklendi) olduğunu işaret eden Kod Tanımlayıcısı'nı (CODE_DESC) seçmek ve 0x8000'e (512 bayt) erişmek için gerekli olan atlama komutunu kullanmaktı ve atlama yönergesiyle sonuçlandı. :

jmp CODE_DESC:0x200 

Ancak bu çalışmaz.

jmp 0x7e0:0x200 

... ya çalışmıyor

Ona, burada eksik bir fikrin var mı? Belki 32-Bit ProtectedMode adresleme şeması ve GDT kullanımı içinde gerekli bir şey anlamadım.

[DÜZENLE] Komple kodu:

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 

cevap

11

kodunda birden sorunlar vardır. senin GDTR.Base

İlk olarak, kod adresi 0 (çünkü org 0 arasında) başlamak üzere derlenmiş olduğundan kod başından GDT ofset içerir. Temel adres fiziksel adres olmalı, göreli bir adres olmamalıdır. IOW, bu org 0'u saklarsanız, CS * 16'yı (= 0x7e00) Base'a eklemelisiniz. çünkü aynı org 0 ait

İkincisi, (bits 32 ve ProtectedMode: sonra) Kodunuzdaki 32 bit uzaklıklar onlar karşılık fiziksel adreslere eşit değildir, onlar fiziksel adreslere daha 0x7e00 az konum. OTOH, GDT'nizde tanımlanan segmentler fiziksel adres 0'da başlar (çünkü GDT girişlerinin taban bölümleri 0'dır) ve 0x7e00'de değildir. Bu, bu segmentleri kodunuz/verilerinizle kullanmaya çalıştığınızda, adreslerin 0x7e00 tarafından eksik kalacağı anlamına gelir. org 0'u tutmak istiyorsanız, GDT'deki temel adresler 0x7e00 olarak ayarlanmalıdır.

Yoksa org 0 0. org 0x7e00 ve daha sonra GDT içinde üsleri olmalıdır değiştirebilir Ve sen, 0 yapacak 0x7e00 tarafından GDTR.Base ayarlamak gerekmez. ... bir kesimi sınırı segment boyutunu eksi 1.

Birkaç fazla puan eşittir

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 

Not Ayrıca geçerli seçicileri veya 0 ile tüm kesimi kayıtlarını yükleyin:

Bu çalışması gerekir yığını kurmak. Eğer orada (veya gerçek modda eski değerler) çöp varsa, kesintiler/istisnalarla oynamaya başladığınızda, daha fazla kaza olur.

Son olarak, ne elf64 bilmiyorum ama diğer modülleri için org şey özen ve tüm oluşturulan adresler adresler yüklemek karşılık emin olmak gerekir. Ve eğer 64-bit modu etkinleştirmeyi planlıyorsanız, yapacak bir ton iş var. Göreceli olarak basit şeylerin üzerinden geçtiğinden 64-bit moduna geçmemenizi tavsiye ederim.

+0

Açıklama için teşekkürler ... İşte bu yüzden bunu yapıyorum - ve x86 referanslarını okuyarak ilk kez sıfırdan yaptığınız zaman bu kadar basit değil :)! Btw: Bu tür konuları tam olarak ele alan öğütler önerebileceğiniz harika bir kitap var mı? –

+1

İyi kitaplar bilmiyorum. Intel ve AMD'nin resmi belgeleri tüm bilgilere sahiptir, sadece okuyabileceğiniz ve hemen her şeyi anlayacağınız tipik bir kitap ya da ders kitabı değildir (btw, Intel'in belgelerinde birçok yazım hatası ve zaman zaman hatalar vardır). Çevrimiçi birçok makale ve eğitici var. Ve her zaman deneyebilirsiniz. Ya da birinin kodunu gör ve soru sor. Şu gruplara bakın: [alt.os.development] (http://groups.google.com/group/alt.os.development/topics), [comp.lang.asm.x86] (http://groups.google .com/grup/comp.lang.asm.x86/başlık). –

+0

Tavsiyeniz için teşekkürler! Buna bir bakacağım! –

3

birkaç nokta. İlk olarak, geçerli kodunuz teknik olarak korumalı moda girmez. GDT'den bir tanımlayıcı ile cs yüklenerek korumalı moda girersiniz. cs kaydını doğrudan ayarlayamadığınız için, bunu yapmanın en kolay yolu uzak bir atlamadır. ile mevcut atlama değiştirin:

jmp (CODE_DESC-NULL_DESC):ProtectedMode 

İkinci kodunuzu segmenti için taban 0, değil 0x7e00 olduğunu."Base" kelimesiyle etiketlenmiş dört bayta bakarsanız, hepsi 0'dır. İki seçeneğiniz vardır. GDT'nizi, 0x7e00 tabanına sahip olacak şekilde değiştirin veya tüm korunan mod kodu için 0 değerinin altındaki tüm adresleri değiştirmek için yönergeleri ekleyin.

Her ikisini de yaptıktan sonra, programınızı kullanarak programınıza atlayabilirsiniz. normal bir atlama talimatı. Olduğu gibi size GDT bırakmak seçerseniz, tam adresi kullanabilirsiniz: Eğer kod segmentinin tabanını değiştirmeyi seçerseniz

jmp 0x8000 

, o adres göre kullanmanız gerekecektir.

jmp 0x200 

More information about the GDT
More information about entering protected mode

+0

Cevabınız için teşekkür ederiz. OK - "jmp CODE_DESC: ProtectedMode" atlama komutunu kullanırken CPU üçlü hataları ve sıfırlar (bu atlama yönü bir yere atlamakta olduğu için). "jmp ProtectedMode" doğru etikete atlar ve sistemi durdurur. Bu GDT temel problemi ile ilişkili olabileceğinden GDT'yi değiştireceğim ve tekrar deneyeceğim. Hızlı yanıtın için teşekkürler !! –

+0

@ughoavgfhw Bunu mu demek istediniz: jmp (CODE_DESC-NULL_DESC): ProtectedMode'? – Nayuki

+0

@NayukiMinase Bunu yakaladığın için şimdiden ofset olduğunu farz ettim. – ughoavgfhw