微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

汇编程序 - 执行 LGDT 指令后 PC 崩溃

如何解决汇编程序 - 执行 LGDT 指令后 PC 崩溃

内容

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

环境

  • cpu:intel(r) core(tm) 17-8550U
  • 机器:虚拟盒子
  • 辅助存储:软盘
  • 编译器:2020 年 8 月 28 日编译的 NASM 版本 2.15.05

代码

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,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,'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

小改动:这里的第一个词是限制而不是大小。你应该写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
,

这是英特尔方面的硬件错误,因此无法解决问题。 我什至不能像规范中那样做。再见,暂时,我会因为我在它上花费的时间而怨恨英特尔。

我提到的规格 enter link description here

9.9.1 切换到保护模式

从实模式切换到保护模式之前,系统数据结构和代码模块的最小集合 必须加载到内存中,如第 9.8 节“保护模式操作的软件初始化”中所述。 一旦创建了这些表,软件初始化代码就可以切换到保护模式。 通过执行设置 CR0 寄存器中的 PE 标志的 MOV CR0 指令进入保护模式。 (在里面 同样的指令,寄存器 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 指令之后,立即执行远 JMP 或远 CALL 指令。 (这个操作是 通常是对指令流中下一条指令的远跳转或调用。)
  5. 紧跟在 MOV CR0 指令之后的 JMP 或 CALL 指令改变了执行流程和 序列化处理器。
  6. 如果启用分页,则 MOV CR0 指令和 JMP 或 CALL 指令的代码必须来自 身份映射的页面(即跳转前的线性地址与物理地址相同) 启用分页和保护模式后)。 JMP 或 CALL 指令的目标指令不 需要进行身份映射。
  7. 如果要使用本地描述符表,请执行 LLDT 指令以加载段选择器 LDTR 寄存器中的 LDT。
  8. 执行 LTR 指令将带有段选择器的任务寄存器加载到初始保护模式任务 或可写的内存区域,可用于在任务开关上存储 TSS 信息。
  9. 进入保护模式后,段寄存器继续保存它们在实地址中的内容 模式。第 4 步中的 JMP 或 CALL 指令复位 CS 寄存器。执行以下操作之一以 更新剩余段寄存器的内容。 — 重新加载段寄存器 DS、SS、ES、FS 和 GS。如果 ES、FS 和/或 GS 寄存器不会被 使用,用空选择器加载它们。 — 对新任务执行 JMP 或 CALL 指令,这会自动重置段的值 注册并分支到新的代码段。
  10. 执行 LIDT 指令以使用保护模式 IDT 的地址和限制加载 IDTR 寄存器。
  11. 执行 STI 指令以启用可屏蔽硬件中断并执行必要的硬件 使能 NMI 中断的操作。 如果上述第 3 步和第 4 步之间存在其他指令,则可能会发生随机故障。失败将很容易被看到 在某些情况下,例如当在第 3 步和第 4 步之间插入引用内存的指令时 系统管理模式。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。