1

我正在尝试简化包含 6 个段的 GDT 表,但其中 2 个段是真正必要的(根据我收集的内容)。我无法使更改生效。

代码来自克伦威尔,一个 Xbox(原始)引导加载程序。CPU 是 Pentium III 。没有用户空间的概念,所以一切都应该在权限级别为 0 的段上运行。我想从一个具有单个 code32 和单个 data32 段的平面模型开始。

这是相关的原始工作代码:

    .code32

.section .text, "ax"
     .org 0x00
     jmp    start_linux

.global Cromwellconfig
Cromwellconfig:
    .org 0x0c
    // Space for the SHA1 checksum
    .org 0x20   

    // The Value positions are fixed, do not change them, used everywhere
    .long 0x0   // 0x20 if XBE, then this bit is 0, if Cromwell mode, the bit is set to 1 by the Startuploader
    .long 0x0   // 0x24 ImageRetryLoads
    .long 0x0   // 0x28 Bank, from where Loaded
    .long 0x0   // 0x2C 0 .. Bios = 256 k, 1 .. Bios = 1MB
    .long 0x0   // 0x30 free
    .long _end_complete_rom       // 0x34 free
    .long 0x0       // 0x38 free
    .long 0x0   // free

.align 16
tableGdt:
    .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x00 dummy
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 // 0x08 code32
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 // 0x10 code32
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 // 0x18 data32
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0x8f, 0x00 // 0x20 code16 (8f indicates 4K granularity, ie, huge limit)
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x8f, 0x00 // 0x28 data16

tableGdtDescriptor:
    // This is the GDT header having 8 bytes
    .word tableGdtDescriptor-tableGdt  // 0x30 byte GDT
    .long GDT_LOC                      // GDT located at 0xA0000
    .word 0                            // Padding
tableGdtEnd:

.align 16
tableIdtDescriptor:

    .word 2048
    .long IDT_LOC                      // IDT located at 0xB0000
    .word 0     // fill Word, so we get aligned again

        // We are dword aligned now

.align 16        
    .globl start_linux
start_linux:

    // Make SURE the IRQs are turned off
    cli

    // kill the cache  = Disable bit 30 + 29 = CD + NW
    // CD = Cache Disable (disable = 1)
    // NW Not write through (disable = 1)
    // Protected mode enabled
    mov     $0x60010033, %eax
    mov %eax, %cr0
    wbinvd

    // Flush the TLB
    xor %eax, %eax
    mov %eax, %cr3

    // We kill the Local Descriptor Table
    xor %eax, %eax
    lldt    %ax

    // DR6/DR7: Clear the debug registers
    xor %eax, %eax
    mov %eax, %dr6
    mov %eax, %dr7
    mov %eax, %dr0
    mov %eax, %dr1
    mov %eax, %dr2
    mov %eax, %dr3


    // IMPORTANT!  Linux expects the GDT located at a specific position,
    // 0xA0000, so we have to move it there.

    // Copy the GDT to its final location
    movl $GDT_LOC, %edi
    movl $tableGdt, %esi
    movl $(tableGdtEnd-tableGdt)/4, %ecx
    // Moving (tableGdtEnd-tableGdt)/4 DWORDS from &tableGdt to &GDT_LOC
    rep movsl

    // Load the new GDT (bits0-15: Table limit, bits16-47: Base address)
    lgdt GDT_LOC+(tableGdtDescriptor-tableGdt)

    // Kill the LDT, if any
    xor %eax, %eax
    lldt %ax

    // Reload CS as 0010 from the new GDT using a far jump
    jmp $0x010, $reload_cs

reload_cs:

    // CS is now a valid entry in the GDT.  Set SS, DS, and ES to valid
    // descriptors, but clear FS and GS as they are not necessary.

    // Set SS, DS, and ES to a data32 segment with maximum limit.
    movw $0x0018, %ax
    mov %eax, %ss
    mov %eax, %ds
    mov %eax, %es

    // Clear FS and GS
    xor %eax, %eax
    mov %eax, %fs
    mov %eax, %gs

将上面代码中的远跳转更改为

jmp $0x008, $reload_cs

顺便说一句,也可以正常工作。

如您所见,保护模式在开始时启用。

我想修剪 GDT 在 0x08 处有一个 code32 段,在 0x10 处有一个 data32 段。这是我对此的看法;这不起作用:

    .code32

.section .text, "ax"
     .org 0x00
     jmp    start_linux

.global Cromwellconfig
Cromwellconfig:
    .org 0x0c
    // Space for the SHA1 checksum
    .org 0x20   

    // The Value positions are fixed, do not change them, used everywhere
    .long 0x0   // 0x20 if XBE, then this bit is 0, if Cromwell mode, the bit is set to 1 by the Startuploader
    .long 0x0   // 0x24 ImageRetryLoads
    .long 0x0   // 0x28 Bank, from where Loaded
    .long 0x0   // 0x2C 0 .. Bios = 256 k, 1 .. Bios = 1MB
    .long 0x0   // 0x30 free
    .long _end_complete_rom       // 0x34 free
    .long 0x0       // 0x38 free
    .long 0x0   // free

.align 16
tableGdt:
    .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x00 dummy
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 // 0x08 code32
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 // 0x10 data32

tableGdtDescriptor:
    // This is the GDT header having 8 bytes
    .word tableGdtDescriptor-tableGdt  // 0x18 byte GDT
    .long GDT_LOC                      // GDT located at 0xA0000
    .word 0                            // Padding
tableGdtEnd:

.align 16
tableIdtDescriptor:

    .word 2048
    .long IDT_LOC                      // IDT located at 0xB0000
    .word 0     // fill Word, so we get aligned again

        // We are dword aligned now

.align 16        
    .globl start_linux
start_linux:

    // Make SURE the IRQs are turned off
    cli

    // kill the cache  = Disable bit 30 + 29 = CD + NW
    // CD = Cache Disable (disable = 1)
    // NW Not write through (disable = 1)
    // Protected mode enabled
    mov     $0x60010033, %eax
    mov %eax, %cr0
    wbinvd

    // Flush the TLB
    xor %eax, %eax
    mov %eax, %cr3

    // We kill the Local Descriptor Table
    xor %eax, %eax
    lldt    %ax

    // DR6/DR7: Clear the debug registers
    xor %eax, %eax
    mov %eax, %dr6
    mov %eax, %dr7
    mov %eax, %dr0
    mov %eax, %dr1
    mov %eax, %dr2
    mov %eax, %dr3


    // IMPORTANT!  Linux expects the GDT located at a specific position,
    // 0xA0000, so we have to move it there.

    // Copy the GDT to its final location
    movl $GDT_LOC, %edi
    movl $tableGdt, %esi
    movl $(tableGdtEnd-tableGdt)/4, %ecx
    // Moving (tableGdtEnd-tableGdt)/4 DWORDS from &tableGdt to &GDT_LOC
    rep movsl

    // Load the new GDT (bits0-15: Table limit, bits16-47: Base address)
    lgdt GDT_LOC+(tableGdtDescriptor-tableGdt)

    // Kill the LDT, if any
    xor %eax, %eax
    lldt %ax

    // Reload CS as 0008 from the new GDT using a far jump
    jmp $0x008, $reload_cs

reload_cs:

    // CS is now a valid entry in the GDT.  Set SS, DS, and ES to valid
    // descriptors, but clear FS and GS as they are not necessary.

    // Set SS, DS, and ES to a data32 segment with maximum limit.
    movw $0x0010, %ax
    mov %eax, %ss
    mov %eax, %ds
    mov %eax, %es

    // Clear FS and GS
    xor %eax, %eax
    mov %eax, %fs
    mov %eax, %gs

任何人都可以发现为什么它不起作用?

我自己无法找到答案的额外问题:

  1. 首先,在“tableGdtDescriptor:”处,限制值(第一个单词)不应该是表格的大小 - 1 吗?那么这里的值应该是“tableGdtDescriptor-tableGdt - 1”吗?如果是这样,为什么它在原始代码中工作?(我的假设是这个值是任何超过 47 字节(6 段 - 1 字节)的值都会恢复到 47 字节。
  2. 如果紧接着强制 16 位对齐,为什么“tableGdtDescriptor”字段在末尾有填充?似乎没有必要。纯粹是为了好习惯?
  3. 为什么 FS 和 GS 被清除,而不是设置为与 SS、DS 和 ES 相同的值?所有在线示例都将这些寄存器设置为相同的段偏移量。为什么这里做的不一样?
4

1 回答 1

2

事实证明,问题在于填充 IDT。我将每个 IDT 条目都指向 GDT 中偏移量 0x10 处的代码段,因此我需要偏移量 0x10 处的编码段。

这是我稍微简化的固定代码:

    .code32

.section .text, "ax"
         .org 0x00
         jmp    start_linux

.global Cromwellconfig
Cromwellconfig:
    .org 0x0c
        // Space for the SHA1 checksum
        .org 0x20   

        // The Value positions are fixed, do not change them, used everywhere
        .long 0x0   // 0x20 if XBE, then this bit is 0, if Cromwell mode, the bit is set to 1 by the Startuploader
        .long 0x0   // 0x24 ImageRetryLoads
        .long 0x0   // 0x28 Bank, from where Loaded
        .long 0x0   // 0x2C 0 .. Bios = 256 k, 1 .. Bios = 1MB
        .long 0x0   // 0x30 free
        .long _end_complete_rom       // 0x34 free
        .long 0x0       // 0x38 free
        .long 0x0   // free

.align 16
tableGdt:
    .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x00 dummy
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9b, 0xcf, 0x00 // 0x08 code32
    .byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xcf, 0x00 // 0x10 data32

tableGdtDescriptor:
    // This is the GDT header having 8 bytes
    .word tableGdtDescriptor-tableGdt - 1  // Size - 1byte
    .long tableGdt                      // GDT location
    .word 0                            // Padding
tableGdtEnd:

.align 16
tableIdtDescriptor:

    .word 2048
    .long IDT_LOC                      // IDT located at 0xB0000
    .word 0     // fill Word, so we get aligned again

        // We are dword aligned now

.align 16        
    .globl start_linux
start_linux:

        //Make SURE the IRQs are turned off
    cli

    // kill the cache  = Disable bit 30 + 29 = CD + NW
    // CD = Cache Disable (disable = 1)
    // NW Not write through (disable = 1)
       //   mov     %cr0, %eax
    //orl   $0x60000000, %eax
    mov     $0x60010033, %eax
    mov %eax, %cr0
    wbinvd

    // Flush the TLB
    xor %eax, %eax
    mov %eax, %cr3

    // We kill the Local Descriptor Table
    xor %eax, %eax
    lldt    %ax

    // DR6/DR7: Clear the debug registers
    xor %eax, %eax
    mov %eax, %dr6
    mov %eax, %dr7
    mov %eax, %dr0
    mov %eax, %dr1
    mov %eax, %dr2
    mov %eax, %dr3

    // Load the new GDT
    lgdt tableGdtDescriptor

    // Kill the LDT, if any
    xor %eax, %eax
    lldt %ax

    // Reload CS as 0008 from the new GDT using a far jump
    jmp $0x0008, $reload_cs

reload_cs:

    // CS is now a valid entry in the GDT.  Set SS, DS, and ES to valid
    // descriptors, but clear FS and GS as they are not necessary.

    // Set SS, DS, and ES to a data32 segment with maximum limit.
    movw $0x0010, %ax
    mov %eax, %ss
    mov %eax, %ds
    mov %eax, %es

    // Clear FS and GS
    xor %eax, %eax
    mov %eax, %fs
    mov %eax, %gs

上面修改的代码现在在描述符中设置正确的 GDT 大小(总大小减去 1 个字节)。此外,GDT 不再在内存中的偏移量 0xA0000 处复制。GDT 寄存器现在指向 GDT 的原始位置。

现在,每个 IDT 条目都将其选择器设置为 0x08 以匹配唯一的 code32 段位置。

于 2018-06-29T00:14:20.773 回答