2012-02-04 5 views
10

현재 저수준 프로그래밍 기술을 향상시키기 위해 x86 Assember를 사용 중입니다. 현재 32 비트 보호 모드에서 주소 지정 체계에 약간의 문제가 있습니다.어셈블러가 GDT로 보호 모드로 점프합니다.

상황은 다음

나는 보호 모드로 CPU를 전환하고 코드에 따라 라벨로 점프 0x7e0로드 프로그램이 있습니다

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

jmp ProtectedMode 


[...] 

bits 32 

ProtectedMode: 
    .halt: 
     hlt 
     jmp .halt 

이 절대적으로 좋은 때문에 작동을 멀리. "jmp ProtectedMode"는 프리 페치 대기열을 지우는 명시 적 먼 점프없이 작동합니다.이 프로그램은 오프셋 0 (처음에는 org 0)이로드되어 코드 세그먼트가 올바른 위치를 가리 키도록합니다.

현재 "ProtectedMode"레이블에서 0x8000으로로드 된 다른 프로그램으로 점프하고 싶습니다. (메모리 덤프로 검사했는데로드 기능이 제대로 작동하고 프로그램이로드되었습니다. 올바르게 0x8000).

CPU가 이제 ProtectedMode에 있고 RealMode가 아니기 때문에 주소 지정 스키마가 다릅니다. ProtectedMode는 설명자 선택기를 사용하여 디스크립터 테이블의 기본 주소와 제한을 조회하여 주어진 오프셋을 추가하고 실제 주소를 검색합니다 (필자의 이해대로). 따라서 ProtectedMode를 시작하기 전에 GDT를 설치해야합니다. 지금까지 이해하지 않았다 무엇

lgdt [gdtr] 

, 어떻게 지금 물리적으로 이동 않습니다를 통해 등록

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

와 GDT에로드됩니다

광산은 다음과 같이보고있다 GDT를 사용하는 ProtectedMode의 주소 0x8000?

첫 번째 생각은 0x7e00 (현재 프로그램이로드 됨)을 가리켜 야하는 코드 설명자 (CODE_DESC)를 선택하고 0x8000 (512 바이트)에 도달하는 데 필요한 오프셋을 사용하여 점프 명령 :

jmp CODE_DESC:0x200 

그러나이 작동하지 않습니다.

jmp 0x7e0:0x200 

... 중 하나가 작동하지 않습니다

당신은 내가 여기에 놓친 거지 어떤 생각을 가지고 있습니까? 어쩌면 32 비트 ProtectedMode 주소 지정 체계와 GDT의 사용에 필수적인 것을 이해하지 못했을 수도 있습니다.

[편집] 전체 코드 :

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 

답변

11

코드에 여러 문제가 있습니다. 당신의 GDTR.Base

첫째, 당신의 코드가 주소 0 (때문에 org 0의)에서 시작하도록 컴파일되어 있기 때문에 코드의 시작부터 GDT의 오프셋 (offset)가 포함되어 있습니다. 기본 주소는 상대 주소가 아닌 실제 주소 여야합니다. IOW,이 값을 org 0으로 유지하는 경우 에 CS * 16 (= 0x7e00)을 추가해야합니다. 때문에 같은 org 0

둘째, (bits 32ProtectedMode: 이후) 코드의 32 비트 오프셋을 해당 실제 주소와 동일하지 않습니다에, 그들은 실제 주소보다 더 0x7e00 이하입니다. OTOH에서 GDT에 정의 된 세그먼트는 실제 주소 0에서 시작합니다 (GDT 항목의 기본 부분은 0이므로 0x7e00이 아님). 즉, 코드/데이터에 이러한 세그먼트를 사용하려고하면 0x7e00으로 주소가 누락됩니다. org 0을 유지하려면 GDT의 기본 주소를 0x7e00으로 설정해야합니다.

org 0x7e00으로 변경 한 다음 GDT의 기본 값을 0으로 설정할 수 있습니다. 그러면 GDTR.Base를 0x7e00으로 조정할 필요가 없습니다. 0으로 설정하면됩니다. ... 세그먼트 제한 세그먼트 크기에서 1을 뺀

몇 점을 더 같음

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 

주 또한 유효 선택기 또는 0 모든 세그먼트 레지스터를로드

이 작동합니다 스택을 설정하십시오. 거기에 가비지 (또는 리얼 모드의 오래된 값)가 있으면 인터럽트/예외로 재생을 시작하면 더 많은 충돌이 발생합니다.

마지막으로 elf64가 무엇인지는 모르지만 다른 모듈에서는 org 것을 처리하고 생성 된 모든 주소가로드 주소와 일치하는지 확인해야합니다. 64 비트 모드를 사용하려는 경우 할 일이 많습니다. 나는 당신이 비교적 단순한 것들을 넘어서고 있기 때문에 아직 64 비트 모드로 돌진하지 말 것을 권합니다.

+0

설명을 주셔서 감사합니다 ... 이것은 정확히 내가 이것을하고있는 이유입니다 - 그리고 처음에 x86 참고 문헌을 읽음으로써 처음부터 이것을 할 때 그렇게 간단하지 않습니다 :)! Btw : 이러한 주제 유형을 정확히 처리 할 수있는 훌륭한 책이 있습니까? –

+1

나는 좋은 책을 모른다. 인텔과 AMD의 공식 문서는 모든 정보를 담고 있으며, 쉽게 읽을 수있는 책이나 교과서의 일반적인 종류가 아니며, 모든 것을 즉시 이해합니다 (btw, 인텔 오피스의 많은 오타 및 가끔 실수가 있음). 많은 기사 및 온라인 자습서가 있습니다. 그리고 당신은 항상 실험을 할 수 있습니다. 또는 누군가의 코드를보고 질문을하십시오. 다음 그룹을 참조하십시오 : [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

조언 해 주셔서 감사합니다. 나는 그것을 볼 것이다! –

3

몇 가지. 첫째, 현재 코드가 기술적으로 보호 모드로 들어 가지 않습니다. cs에 GDT의 설명자를로드하여 보호 모드로 들어갑니다. cs 레지스터를 직접 설정할 수 없으므로이를 수행하는 가장 쉬운 방법은 원거리 점프를 사용하는 것입니다. 현재 점프를 다음으로 바꿉니다 :

jmp (CODE_DESC-NULL_DESC):ProtectedMode 

둘째, 코드 세그먼트의 기본 값은 0x7e00이 아니라 0입니다."base"라는 단어로 레이블 된 4 바이트를 보면, 모두 0입니다. 두 가지 옵션이 있습니다. GDT의 기본 값을 0x7e00으로 변경하거나 0을 기준으로 모든 보호 모드 코드의로드 주소를 변경하기위한 지시문을 추가하십시오.

이 두 가지 작업을 완료하면 다음을 사용하여 프로그램으로 이동할 수 있습니다. 정상적인 점프 명령. 그대로 당신이 당신의 GDT를 떠나 선택한 경우 전체 주소를 사용합니다 : 당신은 당신의 코드 세그먼트의 기준을 변경하기로 선택한 경우

jmp 0x8000 

을, 당신은 그에게 상대적인 주소를 사용해야합니다.

jmp 0x200 

More information about the GDT
More information about entering protected mode

+0

답장을 보내 주셔서 감사합니다 .. OK - 점프 명령어 "jmp CODE_DESC : ProtectedMode"를 사용하면 CPU가 3 배 오류로 리셋됩니다 (이 점프 방향이 어딘가에 점프하는 것 같기 때문에). "jmp ProtectedMode"가 올바른 레이블로 점프하고 시스템을 정지시킵니다. 이것은 GDT 기본 문제와 관련이있을 수 있으므로 GDT를 변경하고 다시 시도해 보겠습니다. 빠른 응답에 감사드립니다 !! –

+0

@ughoavgfhw'jmp (CODE_DESC-NULL_DESC) : ProtectedMode'를 원하십니까? – Nayuki

+0

@NayukiMinase 그것을 잡아 주셔서 감사합니다. 나는 그들이 이미 오프셋이라고 가정했습니다. – ughoavgfhw