如何在保护模式下设置堆栈段?
How to setup Stack segment in protected mode?
这个问题是我在x86保护模式下用GDT下的selector定义了一个Data和Stack段。当 jmp 进入保护模式时,似乎我可以访问数据部分,但在 push eax 时崩溃。见以下代码:
%include "../inc/descriptor.asm"
%include "../inc/define.asm"
org 7c00h
jmp begin
; -----------------------------------------------------------------------
; Const variable
STACK_BASE EQU 1000000h ; 16M
DATA_BASE EQU 2000000h ; 32M
STACK_SIZE EQU 8000h ; 32K
STACK_LIMIT EQU 1008000h ; 16M + 32K
DATA_SIZE EQU 100000h ; 1M
; GDT and LDT
; Descriptor base limit property
[SECTION .gdt]
GDT: Descriptor 0, 0, 0
LDT_CODE32: Descriptor 0, SEG_CODE32_LEN - 1, DA_C + DA_32
LDT_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW
LDT_STACK: Descriptor STACK_BASE, STACK_SIZE - 1, DA_DRWA + DA_B
LDT_DATA: Descriptor DATA_BASE, DATA_SIZE - 1, DA_DRW
GDTLEN EQU $ - GDT
GDTPTR DW GDTLEN - 1
DD 0
; Selectors
SLT_CODE32 EQU LDT_CODE32 - GDT
SLT_VIDEO EQU LDT_VIDEO - GDT
SLT_STACK EQU LDT_STACK - GDT
SLT_DATA EQU LDT_DATA - GDT
; -----------------------------------------------------------------------
; Real mode code
[SECTION .s16]
[BITS 16]
begin:
mov ax, cs
mov ds, ax
; init 32 bits code section descriptor
xor eax, eax
mov ax, cs
shl eax, 4
add eax, code32
mov word [LDT_CODE32 + 2], ax
shr eax, 16
mov byte [LDT_CODE32 + 4], al
mov byte [LDT_CODE32 + 7], ah
; prepare for loading gdtr
xor eax, eax
mov ax, ds
shl eax, 4
add eax, GDT
mov dword [GDTPTR + 2], eax
lgdt [GDTPTR]
cli
in al, 92h
or al, 10b
out 92h, al
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword SLT_CODE32:0
; protected mode code
[SECTION .s32]
[BITS 32]
code32:
mov ax, SLT_VIDEO
mov gs, ax
mov ax, SLT_STACK
mov ss, ax
mov esp, STACK_LIMIT - 16
mov ax, SLT_DATA
mov ds, ax
mov eax, 012345678h
xor edx, edx
mov [edx], eax
mov edx, [edx]
push eax ; **<= crashed here.**
; ---------------------------------
; PREPARE DEBUG CHAR
mov ax, SLT_VIDEO
mov gs, ax
mov bh, 0ch
mov bl, 'B'
mov esi, (80 * 1 + 1) * 2
mov [gs:esi], bx
jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
push eax
pop ebx
mov eax, DATA_BASE
mov dword [eax], ebx
mov edi, 0
mov esi, (80 * 1 + 1) * 2
call PRINT_DWORD
jmp $
; ---------------------------------
; ; PREPARE DEBUG CHAR
; mov ax, SLT_VIDEO
; mov gs, ax
; mov bh, 0ch
; mov bl, 'B'
; mov esi, (80 * 1 + 1) * 2
; mov [gs:esi], bx
; jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
SEG_CODE32_LEN EQU $ - code32
times 290 - ($ - $$) db 0
dw 0xaa55
; command reference:
; nasm protected_mode.asm -o pm.bin
; dd if=pm.bin of=pm.img bs=512 count=1
Descriptor.asm:
;
; Descriptor base, limit, attr
; base: dd
; limit: dd low 20 bits available
; attr: dw low nibble of higher byte always 0
;
%macro Descriptor 3
dw %2 & 0FFFFh
dw %1 & 0FFFFh
db (%1 >> 16) & 0FFh
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
db (%1 >> 24) & 0FFh
%endmacro
;
Define.asm:
;
DA_32 EQU 4000h
DA_DRW EQU 92h
DA_DRWA EQU 93h
DA_C EQU 98h
DA_B EQU DA_32
DA_ELEMENT_4K EQU 8000h
;
; Paging Entry Attribute
PG_P EQU 1
PG_RW_W EQU 2
PG_US_U EQU 4
由于您将段基址设置为 STACK_BASE
,因此您不能将其添加到堆栈指针中。因此,mov esp, STACK_LIMIT - 16
应该是 mov esp, STACK_SIZE - 16
.
PS:您从未设置 cs
,因此您的代码可能会在不为零的系统上中断。
终于解决了为什么那个push指令会导致crash的问题。感谢小丑的帮助。我写在这里是为了其他人可能会遗漏评论。
当定义一个堆栈段时,GDT的属性总是应该设置为随着TYPE 6/7向下增长。段基数 STACK_BASE 定义最高
该段的地址 STACK_SIZE。所以范围应该是从 STACK_BASE - STACK_SIZE 到 STACK_BASE.
然后用STACK_BASE设置esp,就可以使用stack了。
感谢你的帮助。 :)
这个问题是我在x86保护模式下用GDT下的selector定义了一个Data和Stack段。当 jmp 进入保护模式时,似乎我可以访问数据部分,但在 push eax 时崩溃。见以下代码:
%include "../inc/descriptor.asm"
%include "../inc/define.asm"
org 7c00h
jmp begin
; -----------------------------------------------------------------------
; Const variable
STACK_BASE EQU 1000000h ; 16M
DATA_BASE EQU 2000000h ; 32M
STACK_SIZE EQU 8000h ; 32K
STACK_LIMIT EQU 1008000h ; 16M + 32K
DATA_SIZE EQU 100000h ; 1M
; GDT and LDT
; Descriptor base limit property
[SECTION .gdt]
GDT: Descriptor 0, 0, 0
LDT_CODE32: Descriptor 0, SEG_CODE32_LEN - 1, DA_C + DA_32
LDT_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW
LDT_STACK: Descriptor STACK_BASE, STACK_SIZE - 1, DA_DRWA + DA_B
LDT_DATA: Descriptor DATA_BASE, DATA_SIZE - 1, DA_DRW
GDTLEN EQU $ - GDT
GDTPTR DW GDTLEN - 1
DD 0
; Selectors
SLT_CODE32 EQU LDT_CODE32 - GDT
SLT_VIDEO EQU LDT_VIDEO - GDT
SLT_STACK EQU LDT_STACK - GDT
SLT_DATA EQU LDT_DATA - GDT
; -----------------------------------------------------------------------
; Real mode code
[SECTION .s16]
[BITS 16]
begin:
mov ax, cs
mov ds, ax
; init 32 bits code section descriptor
xor eax, eax
mov ax, cs
shl eax, 4
add eax, code32
mov word [LDT_CODE32 + 2], ax
shr eax, 16
mov byte [LDT_CODE32 + 4], al
mov byte [LDT_CODE32 + 7], ah
; prepare for loading gdtr
xor eax, eax
mov ax, ds
shl eax, 4
add eax, GDT
mov dword [GDTPTR + 2], eax
lgdt [GDTPTR]
cli
in al, 92h
or al, 10b
out 92h, al
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword SLT_CODE32:0
; protected mode code
[SECTION .s32]
[BITS 32]
code32:
mov ax, SLT_VIDEO
mov gs, ax
mov ax, SLT_STACK
mov ss, ax
mov esp, STACK_LIMIT - 16
mov ax, SLT_DATA
mov ds, ax
mov eax, 012345678h
xor edx, edx
mov [edx], eax
mov edx, [edx]
push eax ; **<= crashed here.**
; ---------------------------------
; PREPARE DEBUG CHAR
mov ax, SLT_VIDEO
mov gs, ax
mov bh, 0ch
mov bl, 'B'
mov esi, (80 * 1 + 1) * 2
mov [gs:esi], bx
jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
push eax
pop ebx
mov eax, DATA_BASE
mov dword [eax], ebx
mov edi, 0
mov esi, (80 * 1 + 1) * 2
call PRINT_DWORD
jmp $
; ---------------------------------
; ; PREPARE DEBUG CHAR
; mov ax, SLT_VIDEO
; mov gs, ax
; mov bh, 0ch
; mov bl, 'B'
; mov esi, (80 * 1 + 1) * 2
; mov [gs:esi], bx
; jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
SEG_CODE32_LEN EQU $ - code32
times 290 - ($ - $$) db 0
dw 0xaa55
; command reference:
; nasm protected_mode.asm -o pm.bin
; dd if=pm.bin of=pm.img bs=512 count=1
Descriptor.asm:
;
; Descriptor base, limit, attr
; base: dd
; limit: dd low 20 bits available
; attr: dw low nibble of higher byte always 0
;
%macro Descriptor 3
dw %2 & 0FFFFh
dw %1 & 0FFFFh
db (%1 >> 16) & 0FFh
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
db (%1 >> 24) & 0FFh
%endmacro
;
Define.asm:
;
DA_32 EQU 4000h
DA_DRW EQU 92h
DA_DRWA EQU 93h
DA_C EQU 98h
DA_B EQU DA_32
DA_ELEMENT_4K EQU 8000h
;
; Paging Entry Attribute
PG_P EQU 1
PG_RW_W EQU 2
PG_US_U EQU 4
由于您将段基址设置为 STACK_BASE
,因此您不能将其添加到堆栈指针中。因此,mov esp, STACK_LIMIT - 16
应该是 mov esp, STACK_SIZE - 16
.
PS:您从未设置 cs
,因此您的代码可能会在不为零的系统上中断。
终于解决了为什么那个push指令会导致crash的问题。感谢小丑的帮助。我写在这里是为了其他人可能会遗漏评论。
当定义一个堆栈段时,GDT的属性总是应该设置为随着TYPE 6/7向下增长。段基数 STACK_BASE 定义最高 该段的地址 STACK_SIZE。所以范围应该是从 STACK_BASE - STACK_SIZE 到 STACK_BASE.
然后用STACK_BASE设置esp,就可以使用stack了。 感谢你的帮助。 :)