1

内容

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

环境

  • cpu: 英特尔(r) 核心(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, 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
4

2 回答 2

4
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
于 2021-06-27T13:23:03.347 回答
0

这是英特尔方面的硬件错误,因此无法解决问题。我什至无法按照规范中的方式进行操作。再见,暂时,我会因为我花在它上面的时间而怨恨英特尔。

我提到的规格 在此处输入链接描述

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 指令将 LDT 的段选择器加载到 LDTR 寄存器中。
  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 之间插入引用内存的指令时。
于 2021-07-10T20:20:27.343 回答