Assempler - PC 在执行 LGDT 指令后崩溃

Assempler - PC crashes after execution of LGDT instruction

Contents

我想在执行lgdt指令后用jmp指令跳转到diskette_initialisation,但是崩溃了 是什么原因? 如果知道原因,为什么Intel的设计者cpu要这么做?

Environment

code

bits 16
org 0x7c00
  jmp boot_lodaer
  boot_lodaer:
    cli
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov sp, 0x7c00

    call minimum_gdt_andidt
    bits 16
    call valid_A20
    call diskette_initialisation
    jmp  Protected_Mode

    minimum_gdt:
      bits 32
      lgdt [ndt_setup];https://wiki.osdev.org/GDT_Tutoria
   lidt [ndt_setup:
      ret
      ndt_setup:
        dw 23
        dd gdt_null
      ndt_null:
        dq 0x0:
      ndt_code:
        dw 0xffff
        dw 0x0
        db 0x0
        db 11001111b
        db 10011010b
        db 0x0
      ndt_date:
        dw 0xffff
        dw 0x0
        db 0x0
        db 11001111b
        db 10011010b
        db 0x0
   ndit_end:
    ;Only enable a20. Does not check for anything.
    valid_A20:
      in al, 0x60
      mov al, 0xad; key off
      out 0x60, al

      in al, 0x60
      mov   al,   0xfe; key initialisation
      out   0x60, al

      in al, 0x60
      mov   al,  0xae; key on
      out   0x60, al
      mov al, 0x00
    ret  

    diskette_error:
      mov ah, 0x0e
      mov al, 'E'
      int 0x10
    hlt
    
    diskette_initialisation:
      mov ah, 0x00
      mov dl, 0x00
      int 0x13
      jc diskette_error

      mov ah, 0x0e
      mov al, '2'
      int 0x10
    ret

    Protected_Mode:
      cli 
      mov eax, 1
      mov cr0, eax
    jmp eax:karnel
  kanel:
  hlt
  times   510-($-$$) db 0
dw  0aa55h
org 0x7e00

因为这个指令,你代码中的所有位移都会出错!

你需要:ORG 0x7C00


mov ss, ax

如果您不打算同时指定堆栈指针 SP,则不要更改 SS。您很有可能继续使用现有堆栈。


gdt_setup:
   dw 24
   dd gdt_null

小改动:这里的第一个词是 limit 而不是大小。你应该写 dw 23.


jmp [diskette_initialisation]

diskette_initialisation 处有代码,而不是指针(@fuz 首先注意到)。你需要写 jmp diskette_initialisation


您在描述符中使用了一些错误的值!主要是由于几个字节的反转。以下是 CODE 和 DATA 的正确设置:

gdt_code:
    dw 0xFFFF
    dw 0
    db 0
    db 10011010b
    db 11001111b
    db 0
gdt_data:
    dw 0xFFFF
    dw 0
    db 0
    db 10010010b
    db 11001111b
    db 0

这是intel方面的硬件bug,无法解决。 我什至不能像specification.Goodbye那样做,暂时,我会因为我花在它上面的时间而怨恨英特尔。

我参考的规格 enter link description here

9.9.1 Switching to Protected Mode

在从实模式切换到保护模式之前,系统数据结构和代码模块的最小集合 必须加载到内存中,如第 9.8 节“保护模式操作的软件初始化”中所述。 一旦创建了这些table,软件初始化代码就可以切换到保护模式。 通过执行 MOV CR0 指令进入保护模式,该指令在 CR0 寄存器中设置 PE 标志。 (在里面 相同的指令,可以设置寄存器 CR0 中的 PG 标志以启用分页。)保护模式下的执行开始于 CPL 为 0。 Intel 64 和 IA-32 处理器对切换到保护模式的要求略有不同。确保 与 Intel 64 和 IA-32 处理器的向上和向下代码兼容性,我们建议您遵循 这些步骤:

  1. 禁用中断。 CLI 指令禁用可屏蔽硬件中断。可以禁用 NMI 中断 与外部电路。 (软件必须保证在运行期间不产生异常或中断 模式切换操作。)
  2. 执行LGDT指令将GDT的基地址加载到GDTR寄存器
  3. 执行 MOV CR0 指令,在控制寄存器 CR0 中设置 PE 标志(以及可选的 PG 标志)。
  4. 紧接着MOV CR0 指令,执行far JMP 或far CALL 指令。 (这个操作是 通常是远跳转或调用指令流中的下一条指令。)
  5. MOV CR0指令后的JMP或CALL指令改变了执行流程和 序列化处理器。
  6. 如果启用分页,MOV CR0指令和JMP或CALL指令的代码必须来自 恒等映射的页面(即跳转前的线性地址与物理地址相同 启用分页和保护模式后)。 JMP 或 CALL 指令的目标指令不 需要进行身份映射。
  7. 如果要使用局部描述符table,执行LLDT指令加载段选择器 LDTR寄存器中的LDT。
  8. 执行LTR指令加载带段选择器的任务寄存器到初始保护模式任务 或 writable 内存区域,可用于在任务切换时存储 TSS 信息。
  9. 进入保护模式后,段寄存器继续保存它们在实地址中的内容 模式。第 4 步中的 JMP 或 CALL 指令重置 CS 寄存器。执行以下操作之一以 更新剩余段寄存器的内容。 — 重新加载段寄存器 DS、SS、ES、FS 和 GS。如果 ES, FS, and/or GS 寄存器不会被 使用,用空选择器加载它们。 — 对新任务执行 JMP 或 CALL 指令,自动重置段的值 注册并跳转到一个新的代码段。
  10. 执行LIDT指令将保护模式IDT的地址和限制加载到IDTR寄存器。
  11. 执行STI指令启用可屏蔽硬件中断并执行必要的硬件 使能 NMI 中断的操作。 如果上述步骤 3 和 4 之间存在其他说明,则可能会发生随机故障。失败很容易在 在某些情况下,例如当引用内存的指令被插入到步骤 3 和 4 之间时 系统管理模式。