Assempler - PC 在执行 LGDT 指令后崩溃
Assempler - PC crashes after execution of LGDT instruction
Contents
我想在执行lgdt指令后用jmp指令跳转到diskette_initialisation,但是崩溃了
是什么原因?
如果知道原因,为什么Intel的设计者cpu要这么做?
Environment
- cpu: 英特尔(r) 核心(tm) 17-8550U
- 机器:虚拟盒子
- 辅助存储:软盘
- 编译器:NASM 2.15.05 版编译于 2020 年 8 月 28 日
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 处理器的向上和向下代码兼容性,我们建议您遵循
这些步骤:
- 禁用中断。 CLI 指令禁用可屏蔽硬件中断。可以禁用 NMI 中断
与外部电路。 (软件必须保证在运行期间不产生异常或中断
模式切换操作。)
- 执行LGDT指令将GDT的基地址加载到GDTR寄存器
- 执行 MOV CR0 指令,在控制寄存器 CR0 中设置 PE 标志(以及可选的 PG 标志)。
- 紧接着MOV CR0 指令,执行far JMP 或far CALL 指令。 (这个操作是
通常是远跳转或调用指令流中的下一条指令。)
- MOV CR0指令后的JMP或CALL指令改变了执行流程和
序列化处理器。
- 如果启用分页,MOV CR0指令和JMP或CALL指令的代码必须来自
恒等映射的页面(即跳转前的线性地址与物理地址相同
启用分页和保护模式后)。 JMP 或 CALL 指令的目标指令不
需要进行身份映射。
- 如果要使用局部描述符table,执行LLDT指令加载段选择器
LDTR寄存器中的LDT。
- 执行LTR指令加载带段选择器的任务寄存器到初始保护模式任务
或 writable 内存区域,可用于在任务切换时存储 TSS 信息。
- 进入保护模式后,段寄存器继续保存它们在实地址中的内容
模式。第 4 步中的 JMP 或 CALL 指令重置 CS 寄存器。执行以下操作之一以
更新剩余段寄存器的内容。
— 重新加载段寄存器 DS、SS、ES、FS 和 GS。如果 ES, FS, and/or GS 寄存器不会被
使用,用空选择器加载它们。
— 对新任务执行 JMP 或 CALL 指令,自动重置段的值
注册并跳转到一个新的代码段。
- 执行LIDT指令将保护模式IDT的地址和限制加载到IDTR寄存器。
- 执行STI指令启用可屏蔽硬件中断并执行必要的硬件
使能 NMI 中断的操作。
如果上述步骤 3 和 4 之间存在其他说明,则可能会发生随机故障。失败很容易在
在某些情况下,例如当引用内存的指令被插入到步骤 3 和 4 之间时
系统管理模式。
Contents
我想在执行lgdt指令后用jmp指令跳转到diskette_initialisation,但是崩溃了 是什么原因? 如果知道原因,为什么Intel的设计者cpu要这么做?
Environment
- cpu: 英特尔(r) 核心(tm) 17-8550U
- 机器:虚拟盒子
- 辅助存储:软盘
- 编译器:NASM 2.15.05 版编译于 2020 年 8 月 28 日
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 处理器的向上和向下代码兼容性,我们建议您遵循 这些步骤:
- 禁用中断。 CLI 指令禁用可屏蔽硬件中断。可以禁用 NMI 中断 与外部电路。 (软件必须保证在运行期间不产生异常或中断 模式切换操作。)
- 执行LGDT指令将GDT的基地址加载到GDTR寄存器
- 执行 MOV CR0 指令,在控制寄存器 CR0 中设置 PE 标志(以及可选的 PG 标志)。
- 紧接着MOV CR0 指令,执行far JMP 或far CALL 指令。 (这个操作是 通常是远跳转或调用指令流中的下一条指令。)
- MOV CR0指令后的JMP或CALL指令改变了执行流程和 序列化处理器。
- 如果启用分页,MOV CR0指令和JMP或CALL指令的代码必须来自 恒等映射的页面(即跳转前的线性地址与物理地址相同 启用分页和保护模式后)。 JMP 或 CALL 指令的目标指令不 需要进行身份映射。
- 如果要使用局部描述符table,执行LLDT指令加载段选择器 LDTR寄存器中的LDT。
- 执行LTR指令加载带段选择器的任务寄存器到初始保护模式任务 或 writable 内存区域,可用于在任务切换时存储 TSS 信息。
- 进入保护模式后,段寄存器继续保存它们在实地址中的内容 模式。第 4 步中的 JMP 或 CALL 指令重置 CS 寄存器。执行以下操作之一以 更新剩余段寄存器的内容。 — 重新加载段寄存器 DS、SS、ES、FS 和 GS。如果 ES, FS, and/or GS 寄存器不会被 使用,用空选择器加载它们。 — 对新任务执行 JMP 或 CALL 指令,自动重置段的值 注册并跳转到一个新的代码段。
- 执行LIDT指令将保护模式IDT的地址和限制加载到IDTR寄存器。
- 执行STI指令启用可屏蔽硬件中断并执行必要的硬件 使能 NMI 中断的操作。 如果上述步骤 3 和 4 之间存在其他说明,则可能会发生随机故障。失败很容易在 在某些情况下,例如当引用内存的指令被插入到步骤 3 和 4 之间时 系统管理模式。