BackupGGCode / writeos

Write Your Own OS with Free and Open Source Software (Chinese)
1 stars 1 forks source link

对于例子“chapter3/2/loader.S”的理解,有误的话麻烦指出,谢谢 #9

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
/* chapter3/2/loader.S

#include "pm.h"

.code16
.text
    jmp LABEL_BEGIN     /* jump over the .data section. */

/* Global Descriptor Table */
LABEL_GDT:          Descriptor        0,                  0, 0
LABEL_DESC_CODE32:  Descriptor        0, (SegCode32Len - 1), (DA_C + DA_32)
LABEL_DESC_DATA:    Descriptor        0,      (DataLen - 1), DA_DRW
LABEL_DESC_STACK:   Descriptor        0,         TopOfStack, (DA_DRWA + DA_32)
LABEL_DESC_VIDEO:   Descriptor  0xB8000,             0xffff, DA_DRW
LABEL_DESC_LDT:     Descriptor        0,       (LDTLen - 1), DA_LDT

==============================================================================
理解1:
LABEL_GDT 为GDT描述符表,主要用于存放GDT的段首地址。
LABEL_DESC_CODE32 为GDT描述符表中的一个描述符。
LABEL_DESC_DATA 
为数据段描述符,主要用于存放数据段的段首地址
LABEL_DESC_STACK 
为堆栈段描述符,主要用于存放堆栈段的段首地址,在本例��
�只作
扩展保留用。
LABEL_DESC_VIDEO 
为显示字符串而设的段描述,主要用于存放该段的首地址
LABEL_DESC_LDT 为LDT描述符,主要用于存放LDT的段首地址

以上6个段描述符都是GDT表里面的段描述符。
==============================================================================

.set GdtLen, (. - LABEL_GDT)  /* GDT Length */

GdtPtr: .2byte  (GdtLen - 1)  /* GDT Limit */
        .4byte  0             /* GDT Base */

/* GDT Selector(TI flag clear) */
.set    SelectorCode32, (LABEL_DESC_CODE32 - LABEL_GDT)
.set    SelectorData,   (LABEL_DESC_DATA   - LABEL_GDT)
.set    SelectorStack,  (LABEL_DESC_STACK  - LABEL_GDT)
.set    SelectorVideo,  (LABEL_DESC_VIDEO  - LABEL_GDT)
.set    SelectorLDT,    (LABEL_DESC_LDT    - LABEL_GDT)

==============================================================================
理解2:
在选择子头3位都假定为0的情况下,
SelectorCode32 
的值为LABEL_DESC_CODE32描述符与GDT段描述符之间的偏移量。
SelectorData   
的值为LABEL_DESC_DATA描述符与GDT段描述符之间的偏移量。
SelectorStack  
的值为LABEL_DESC_STACK描述符与GDT段描述符之间的偏移量。
SelectorVideo  
的值为LABEL_DESC_VIDEO描述符与GDT段描述符之间的偏移量。
SelectorLDT    
的值为LABEL_DESC_LDT描述符与GDT段描述符之间的偏移量。
==============================================================================

/* LDT segment */
LABEL_LDT:
LABEL_LDT_DESC_CODEA:   Descriptor  0, (CodeALen - 1), (DA_C + DA_32)

.set    LDTLen, (. - LABEL_LDT) /* LDT Length */
/* LDT Selector (TI flag set)*/
.set    SelectorLDTCodeA, (LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL)

==============================================================================
理解3:
段描述符LABEL_LDT_DESC_CODEA是LDT段描述符表里面的一个段描述符
==============================================================================

/* 32-bit global data segment. */
LABEL_DATA:
PMMessage:   .ascii "Welcome to protect mode! ^-^\0"
LDTMessage:  .ascii "Aha, you jumped into a LDT segment.\0"
.set    OffsetPMMessage,  (PMMessage - LABEL_DATA)
.set    OffsetLDTMessage, (LDTMessage - LABEL_DATA)
.set    DataLen,          (. - LABEL_DATA)

==============================================================================
理解4:
在数据段LABEL_DATA中:
定义了2个字符串,PMMessage,LDTMessage,并且设定了他们俩的长
度。
同时,也设定了数据段的长度DataLen,其值为整个数据段的长�
��
==============================================================================

/* 32-bit global stack segment. */
LABEL_STACK:
.space  512, 0
.set    TopOfStack, (. - LABEL_STACK - 1)

/* Program starts here. */
LABEL_BEGIN:
    mov     %cs, %ax    /* Move code segment address(CS) to data segment */
    mov     %ax, %ds    /* register(DS), ES and SS. Because we have      */
    mov     %ax, %es    /* embedded .data section into .code section in  */
    mov     %ax, %ss    /* the start(metioned in the NOTE above).        */

    mov     $0x100, %sp

    /* Initialize 32-bits code segment descriptor. */
    InitDesc LABEL_SEG_CODE32, LABEL_DESC_CODE32

    /* Initialize data segment descriptor. */
    InitDesc LABEL_DATA, LABEL_DESC_DATA

    /* Initialize stack segment descriptor. */
    InitDesc LABEL_STACK, LABEL_DESC_STACK

    /* Initialize LDT descriptor in GDT. */
    InitDesc LABEL_LDT, LABEL_DESC_LDT

    /* Initialize code A descriptor in LDT. */
    InitDesc LABEL_CODEA, LABEL_LDT_DESC_CODEA

==============================================================================

理解5:
1、初始化段描述符LABEL_DESC_CODE32,即将代码段中标号LABEL_SEG_C
ODE32的地址
(包括
代码段首地址和它在代码段中的段内偏移)作为段描述符LABEL
_DESC_CODE32的段地址存于
段描述符LABEL_DESC_CODE32。
2、同理于
LABEL_DESC_DATA,LABEL_DESC_STACK,LABEL_DESC_LDT,LABEL_LDT_DESC_CODEA
==============================================================================

    /* Prepared for loading GDTR */
    xor     %eax, %eax
    mov     %ds, %ax
    shl     $4, %eax
    add     $(LABEL_GDT), %eax      /* eax <- gdt base*/
    movl    %eax, (GdtPtr + 2)

    /* Load GDTR(Global Descriptor Table Register) */
    lgdtw   GdtPtr

==============================================================================
理解6:
将数据段寄存器中存有数据段地址作为段描述符表LABEL_GDT 
的首地址并载入。
==============================================================================

    /* Clear Interrupt Flags */
    cli

    /* Open A20 line. */
    inb     $0x92, %al
    orb     $0b00000010, %al
    outb    %al, $0x92

    /* Enable protect mode, PE bit of CR0. */
    movl    %cr0, %eax
    orl     $1, %eax
    movl    %eax, %cr0

    /* Mixed-Size Jump. */
    ljmpl $SelectorCode32, $0       /* Thanks to earthengine@gmail, I got */
                                    /* this mixed-size jump insn of gas.  */

==============================================================================

理解7:
由于选择子$SelectorCode32存放的是LABEL_DESC_CODE32描述符与GDT段描
述符之间的
偏移量,
而GDT段描述符表也已载入(即GDT的首地址CPU已知道),因此��
�合跳转指令直接就跳转至
段描述符LABEL_DESC_CODE32的首地址。
另外,在前面提到的:
InitDesc LABEL_SEG_CODE32, LABEL_DESC_CODE32
如果改成如下:
InitDesc LABEL_GDT, LABEL_DESC_CODE32
那混合跳转指令是不是就也得改成如下?
ljmpl $SelectorGDT, $0
(注:文中并没有$SelectorGDT,这是我杜撰上去的,可以假定如�
��:
.set    SelectorGDT, (LABEL_GDT - LABEL_GDT)
)
==============================================================================

/* 32-bit code segment for LDT */
LABEL_CODEA:
.code32
    mov     $(SelectorVideo), %ax
    mov     %ax, %gs

    movb    $0xC, %ah               /* 0000: Black Back 1100: Red Front */
    xor     %esi, %esi
    xor     %edi, %edi
    movl    $(OffsetLDTMessage), %esi
    movl    $((80 * 12 + 0) * 2), %edi
    cld                         /* Clear DF flag. */

==============================================================================
理解8:
mov     $(SelectorVideo), %ax 
mov     %ax, %gs
将选择子的地址给寄存器%gs并作为字符串的显示用。由于选��
�子SelectorVideo存放的是
LABEL_DESC_VIDEO描述符与GDT段描述符之间的偏移量,而GDT段描述�
��表也已载入(即GDT
的首地址CPU已知道),因此显示字符串时用到的内存段便是��
�段描述符LABEL_DESC_VIDEO
首地址开始的一段内存。这跟0B800H作为ES的起始地址用于显示
字符串的情况一样,
只是这
次的首地址不是0B800H,而是段描述符LABEL_DESC_VIDEO首地址。
==============================================================================

/* Display a string from %esi(string offset) to %edi(video segment). */
CODEA_1:
    lodsb                       /* Load a byte from source */
    test    %al, %al
    jz      CODEA_2
    mov     %ax, %gs:(%edi)
    add     $2, %edi
    jmp     CODEA_1
CODEA_2:

    /* Stop here, infinate loop. */
    jmp     .
.set    CodeALen, (. - LABEL_CODEA)

/* 32-bit code segment for GDT */
LABEL_SEG_CODE32: 
    mov     $(SelectorData), %ax
    mov     %ax, %ds                /* Data segment selector */
    mov     $(SelectorStack), %ax
    mov     %ax, %ss                /* Stack segment selector */
    mov     $(SelectorVideo), %ax
    mov     %ax, %gs                /* Video segment selector(dest) */

    mov     $(TopOfStack), %esp

==============================================================================
理解9:
同理,通过对%ds,%ss,%gs,%esp的初始化,CPU获知了各个段的首地�
��。
==============================================================================

    movb    $0xC, %ah               /* 0000: Black Back 1100: Red Front */
    xor     %esi, %esi
    xor     %edi, %edi
    movl    $(OffsetPMMessage), %esi
    movl    $((80 * 10 + 0) * 2), %edi
    cld                         /* Clear DF flag. */

/* Display a string from %esi(string offset) to %edi(video segment). */
CODE32_1:
    lodsb                       /* Load a byte from source */
    test    %al, %al
    jz      CODE32_2
    mov     %ax, %gs:(%edi)
    add     $2, %edi
    jmp     CODE32_1
CODE32_2:

    mov     $(SelectorLDT), %ax
    lldt    %ax

    ljmp    $(SelectorLDTCodeA), $0

==============================================================================

理解10:
如果LABEL_LDT_DESC_CODEA是LDT表中的一个段描述符,那根据:
InitDesc LABEL_CODEA, LABEL_LDT_DESC_CODEA
得知,该段描述符中所存的段地址是标号LABEL_CODEA的地址。语
句:
ljmp    $(SelectorLDTCodeA), $0
是在加载LDT表后“混合跳转”(是混合跳转吗?但已经在.code
32里面了....)至标号
LABEL_CODEA,
从而实现字符串的显示。另外,程序段:
LABEL_CODEA:
.code32
    mov     $(SelectorVideo), %ax
    mov     %ax, %gs

    movb    $0xC, %ah               /* 0000: Black Back 1100: Red Front */
    xor     %esi, %esi
    xor     %edi, %edi
    movl    $(OffsetLDTMessage), %esi
    movl    $((80 * 12 + 0) * 2), %edi
    cld                         /* Clear DF flag. */

/* Display a string from %esi(string offset) to %edi(video segment). */
CODEA_1:
    lodsb                       /* Load a byte from source */
    test    %al, %al
    jz      CODEA_2
    mov     %ax, %gs:(%edi)
    add     $2, %edi
    jmp     CODEA_1
CODEA_2:

    /* Stop here, infinate loop. */
    jmp     .
.set    CodeALen, (. - LABEL_CODEA)

是用于显示“Aha, you jumped into a LDT 
segment.”,而标号CODEA_2里面的程序段
是用于结束程序用的,是吗?(也就是语句: “jmp     .” )
==============================================================================

/* Get the length of 32-bit segment code. */
.set    SegCode32Len, . - LABEL_SEG_CODE32

以上的各个理解不知道对不对,有误的请指出,谢谢。

Original issue reported on code.google.com by linux...@gmail.com on 26 Sep 2008 at 7:17

GoogleCodeExporter commented 9 years ago
希望与作者共同讨论,只为加深对书中例子的理解。另,对��
�操作系统的编写,本人是一初学
者,期望通过本书的学习能得到提高,因此非常期待与作者��
�广大读者一起讨论,一起提高。学
习之初,有些问题可能会提得比较“幼稚”,希望作者不要��
�弃才好,呵呵。

Original comment by linux...@gmail.com on 27 Sep 2008 at 6:16