Bochs:程序集远跳在伪造的内存区域中丢失(无效的操作码错误)
Bochs: assembly far jump got lost in bogus memory area (invalid opcode error)
我开始用(NASM)汇编开发一个小玩具操作系统,仅供娱乐。
我编写了一个引导加载程序,它从名为 "kernel.sys" 的 FAT12 文件系统中将第一个(也是唯一一个)文件加载到偏移量 0x7E00
处的内存中。
在实模式下,内核仅通过 BIOS 设置适当的视频模式,然后进入 32 位(保护)模式。这就是我的问题所在。
首先我设置了一个GDT,有3个描述符(null,ring 0 code,ring 0 data),直接加载到内存区域0x0500
。然后我使用 LGDT
指令告诉处理器,然后我在 CR0
寄存器中设置 PE
位,我想通过远跳转进入保护模式以设置适当的段(0x08 - GDT 中的代码段)和指令指针。
它的第一个版本是在 QEMU 中工作的,但不是在 Bochs 中。 Bochs 需要在远跳转之前设置段,所以我在我的代码中修改了它:在远跳转之前,我从我的 GDT 中加载带有数据段的选择器。但是,由于 "opcode invalid" 错误,Bochs 仍然无法进入保护模式。
请帮我解决这个错误!
这是我的内核代码:
(请注意,标签 b32
从未到达!)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 16-BIT ENTRY ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
use16
org 0x7e00
jmp start
sys_gdt equ 0x00000500
sys_gdt_ring0c equ 0x00000508
sys_gdt_ring0d equ 0x00000510
sys_gdtr equ 0x00000518
start:
cli
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0x1000
sti
mov ax, 3
int 0x10
set_a20:
in al, 0x64
test al, 2
jnz set_a20
mov al, 0xd1
out 0x64, al
test_a20:
in al, 0x64
test al, 2
jnz test_a20
mov al, 0xdf
out 0x60, al
mov dword [sys_gdt+0], 0
mov dword [sys_gdt+4], 0
mov word [sys_gdt_ring0c+0], 0xffff
mov word [sys_gdt_ring0c+2], 0
mov byte [sys_gdt_ring0c+4], 0
mov byte [sys_gdt_ring0c+5], 10011010b
mov byte [sys_gdt_ring0c+6], 01001111b
mov byte [sys_gdt_ring0c+7], 0
mov word [sys_gdt_ring0d+0], 0xffff
mov word [sys_gdt_ring0d+2], 0
mov byte [sys_gdt_ring0d+4], 0
mov byte [sys_gdt_ring0d+5], 10010010b
mov byte [sys_gdt_ring0d+6], 01001111b
mov byte [sys_gdt_ring0d+7], 0
mov word [sys_gdtr+0], sys_gdtr-sys_gdt-1
mov dword [sys_gdtr+2], sys_gdt
cli
lgdt [sys_gdtr] ;; :96
mov eax, cr0
or eax, 0x1
mov cr0, eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x90000
jmp 0x08:b32
use32
b32:
mov cx, 5
jmp $
这是 Bochs 日志:
00014041550i[BIOS ] Booting from 0000:7c00
00015625085e[CPU0 ] write_virtual_checks(): write beyond limit, r/w
00015625085i[CPU0 ] CPU is in protected mode (active)
00015625085i[CPU0 ] CS.d_b = 32 bit
00015625085i[CPU0 ] SS.d_b = 32 bit
00015625085i[CPU0 ] EFER = 0x00000000
00015625085i[CPU0 ] | RAX=0000000060000010 RBX=0000000000000204
00015625085i[CPU0 ] | RCX=0000000000090000 RDX=0000000000000fff
00015625085i[CPU0 ] | RSP=0000000000090000 RBP=0000000000000000
00015625085i[CPU0 ] | RSI=00000000000e018e RDI=0000000000008000
00015625085i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00015625085i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00015625085i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00015625085i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00015625085i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
00015625085i[CPU0 ] | SEG selector base limit G D
00015625085i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00015625085i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | MSR_FS_BASE:0000000000000000
00015625085i[CPU0 ] | MSR_GS_BASE:0000000000000000
00015625085i[CPU0 ] | RIP=0000000000007ebb (0000000000007eb9)
00015625085i[CPU0 ] | CR0=0x60000011 CR2=0x0000000000000000
00015625085i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00015625085i[CPU0 ] 0x0000000000007eb9>> add byte ptr ds:[eax], al : 0000
00015625085i[CMOS ] Last time is 1459506108 (Fri Apr 1 12:21:48 2016)
00015625085i[ ] restoring default signal behavior
00015625085i[CTRL ] quit_sim called with exit code 1
这是我的引导程序:
use16
jmp start
OEMLabel db 'SYRACUSE'
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224
LogicalSectors dw 2880
MediumByte db 0xf0
SectorsPerFat dw 9
SectorsPerTrack dw 18
Heads dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 41
VolumeID dd 0
VolumeLabel db 'Syracuse1.0'
FileSystem db 'FAT12 '
chs_lba:
sub ax, 2
xor cx, cx
mov cl, byte [SectorsPerCluster]
mul cx
add ax, word [datasector]
ret
lba_chs:
xor dx, dx
div word [SectorsPerTrack]
inc dl
mov byte [absoluteSector], dl
xor dx, dx
div word [Heads]
mov byte [absoluteHead], dl
mov byte [absoluteTrack], al
ret
print:
pusha
mov ah, 0xe
.repeat:
lodsb
cmp al, 0
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
read_sectors:
mov di, 5
.loop:
pusha
call lba_chs
mov ah, 2
mov al, 1
mov ch, byte [absoluteTrack]
mov cl, byte [absoluteSector]
mov dh, byte [absoluteHead]
mov dl, byte [DriveNo]
int 0x13
jnc .done
xor ax, ax
int 0x13
dec di
popa
jnz .loop
int 0x18
.done:
popa
inc ax
add bx, word [BytesPerSector]
loop read_sectors
ret
start:
cli
mov ax, 0x07c0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0
mov ss, ax
mov sp, 0xffff
sti
load_root:
xor cx, cx
xor dx, dx
mov ax, 32
mul word [RootDirEntries]
div word [BytesPerSector]
xchg ax, cx
mov al, byte [NumberOfFats]
mul word [SectorsPerFat]
add ax, word [ReservedForBoot]
mov word [datasector], ax
add word [datasector], cx
mov bx, 0x0200
call read_sectors
mov cx, word [RootDirEntries]
mov di, 0x0200
.loop:
push cx
mov cx, 11
mov si, kernel
push di
rep cmpsb
pop di
je load_fat
pop cx
add di, 32
loop .loop
jmp failure
load_fat:
mov dx, word [di+0x001a]
mov word [cluster], dx
xor ax, ax
mov al, byte [NumberOfFats]
mul word [SectorsPerFat]
mov cx, ax
mov ax, word [ReservedForBoot]
mov bx, 0x0200
call read_sectors
mov ax, 0x7e00
mov es, ax
mov bx, 0x0000
load_kernel:
mov ax, word [cluster]
call chs_lba
xor cx, cx
mov cl, byte [SectorsPerCluster]
call read_sectors
mov ax, word [cluster]
mov cx, ax
mov dx, ax
shr dx, 1
add cx, dx
mov bx, 0x0200
add bx, cx
mov dx, word [bx]
test ax, 1
jnz .odd
.even:
and dx, 0000111111111111b
jmp .done
.odd:
shr dx, 4
.done:
mov word [cluster], dx
cmp dx, 0x0ff0
jb load_kernel
pusha
mov di, 0x7e00
xor ax, ax
mov cx, 512
rep stosb
execute_kernel:
;push word 0x7e00
;push word 0x0000
;retf
jmp 0x7e00:0x0000
failure:
mov si, msg
call print
mov ah, 0
int 0x16
int 0x19
absoluteSector db 0
absoluteHead db 0
absoluteTrack db 0
datasector dw 0
cluster dw 0
kernel db 'KERNEL SYS'
msg db 'MISSING KERNEL. Press any key to reboot...', 0xA, 0xD, 0
times 510-($-$$) db 0
dw 0xAA55
您知道实模式寻址使用 16*segment+offset
作为物理地址,对吗?您在 0x7E00:0000
处加载代码,因此是物理地址 0x7E000
(注意 3 个零)。但是您的内核需要地址 0x7E00
(注意 2 个零)。
你的代码是双重错误的。首先,您实际上跳转到了偏移量 0
,因此您应该使用 org 0
(这是默认设置)。其次,必须为实模式段调整物理地址,即jmp dword 0x8:b32+0x7e000
。这将修复当前代码,但 32 位部分将再次使用错误的 org
。
你正在让自己的生活不必要地复杂化。通常的最佳做法是将代码加载到前 64k 内的地址中,您可以在其中使用段 0 和 16 位偏移量直接映射到实内存和平面保护模式中的物理内存。因此,我建议加载到 0:0x8000
.
我开始用(NASM)汇编开发一个小玩具操作系统,仅供娱乐。
我编写了一个引导加载程序,它从名为 "kernel.sys" 的 FAT12 文件系统中将第一个(也是唯一一个)文件加载到偏移量 0x7E00
处的内存中。
在实模式下,内核仅通过 BIOS 设置适当的视频模式,然后进入 32 位(保护)模式。这就是我的问题所在。
首先我设置了一个GDT,有3个描述符(null,ring 0 code,ring 0 data),直接加载到内存区域0x0500
。然后我使用 LGDT
指令告诉处理器,然后我在 CR0
寄存器中设置 PE
位,我想通过远跳转进入保护模式以设置适当的段(0x08 - GDT 中的代码段)和指令指针。
它的第一个版本是在 QEMU 中工作的,但不是在 Bochs 中。 Bochs 需要在远跳转之前设置段,所以我在我的代码中修改了它:在远跳转之前,我从我的 GDT 中加载带有数据段的选择器。但是,由于 "opcode invalid" 错误,Bochs 仍然无法进入保护模式。
请帮我解决这个错误!
这是我的内核代码:
(请注意,标签 b32
从未到达!)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; 16-BIT ENTRY ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
use16
org 0x7e00
jmp start
sys_gdt equ 0x00000500
sys_gdt_ring0c equ 0x00000508
sys_gdt_ring0d equ 0x00000510
sys_gdtr equ 0x00000518
start:
cli
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0x1000
sti
mov ax, 3
int 0x10
set_a20:
in al, 0x64
test al, 2
jnz set_a20
mov al, 0xd1
out 0x64, al
test_a20:
in al, 0x64
test al, 2
jnz test_a20
mov al, 0xdf
out 0x60, al
mov dword [sys_gdt+0], 0
mov dword [sys_gdt+4], 0
mov word [sys_gdt_ring0c+0], 0xffff
mov word [sys_gdt_ring0c+2], 0
mov byte [sys_gdt_ring0c+4], 0
mov byte [sys_gdt_ring0c+5], 10011010b
mov byte [sys_gdt_ring0c+6], 01001111b
mov byte [sys_gdt_ring0c+7], 0
mov word [sys_gdt_ring0d+0], 0xffff
mov word [sys_gdt_ring0d+2], 0
mov byte [sys_gdt_ring0d+4], 0
mov byte [sys_gdt_ring0d+5], 10010010b
mov byte [sys_gdt_ring0d+6], 01001111b
mov byte [sys_gdt_ring0d+7], 0
mov word [sys_gdtr+0], sys_gdtr-sys_gdt-1
mov dword [sys_gdtr+2], sys_gdt
cli
lgdt [sys_gdtr] ;; :96
mov eax, cr0
or eax, 0x1
mov cr0, eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x90000
jmp 0x08:b32
use32
b32:
mov cx, 5
jmp $
这是 Bochs 日志:
00014041550i[BIOS ] Booting from 0000:7c00
00015625085e[CPU0 ] write_virtual_checks(): write beyond limit, r/w
00015625085i[CPU0 ] CPU is in protected mode (active)
00015625085i[CPU0 ] CS.d_b = 32 bit
00015625085i[CPU0 ] SS.d_b = 32 bit
00015625085i[CPU0 ] EFER = 0x00000000
00015625085i[CPU0 ] | RAX=0000000060000010 RBX=0000000000000204
00015625085i[CPU0 ] | RCX=0000000000090000 RDX=0000000000000fff
00015625085i[CPU0 ] | RSP=0000000000090000 RBP=0000000000000000
00015625085i[CPU0 ] | RSI=00000000000e018e RDI=0000000000008000
00015625085i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00015625085i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00015625085i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00015625085i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00015625085i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
00015625085i[CPU0 ] | SEG selector base limit G D
00015625085i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00015625085i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 0 1
00015625085i[CPU0 ] | MSR_FS_BASE:0000000000000000
00015625085i[CPU0 ] | MSR_GS_BASE:0000000000000000
00015625085i[CPU0 ] | RIP=0000000000007ebb (0000000000007eb9)
00015625085i[CPU0 ] | CR0=0x60000011 CR2=0x0000000000000000
00015625085i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00015625085i[CPU0 ] 0x0000000000007eb9>> add byte ptr ds:[eax], al : 0000
00015625085i[CMOS ] Last time is 1459506108 (Fri Apr 1 12:21:48 2016)
00015625085i[ ] restoring default signal behavior
00015625085i[CTRL ] quit_sim called with exit code 1
这是我的引导程序:
use16
jmp start
OEMLabel db 'SYRACUSE'
BytesPerSector dw 512
SectorsPerCluster db 1
ReservedForBoot dw 1
NumberOfFats db 2
RootDirEntries dw 224
LogicalSectors dw 2880
MediumByte db 0xf0
SectorsPerFat dw 9
SectorsPerTrack dw 18
Heads dw 2
HiddenSectors dd 0
LargeSectors dd 0
DriveNo dw 0
Signature db 41
VolumeID dd 0
VolumeLabel db 'Syracuse1.0'
FileSystem db 'FAT12 '
chs_lba:
sub ax, 2
xor cx, cx
mov cl, byte [SectorsPerCluster]
mul cx
add ax, word [datasector]
ret
lba_chs:
xor dx, dx
div word [SectorsPerTrack]
inc dl
mov byte [absoluteSector], dl
xor dx, dx
div word [Heads]
mov byte [absoluteHead], dl
mov byte [absoluteTrack], al
ret
print:
pusha
mov ah, 0xe
.repeat:
lodsb
cmp al, 0
je .done
int 0x10
jmp short .repeat
.done:
popa
ret
read_sectors:
mov di, 5
.loop:
pusha
call lba_chs
mov ah, 2
mov al, 1
mov ch, byte [absoluteTrack]
mov cl, byte [absoluteSector]
mov dh, byte [absoluteHead]
mov dl, byte [DriveNo]
int 0x13
jnc .done
xor ax, ax
int 0x13
dec di
popa
jnz .loop
int 0x18
.done:
popa
inc ax
add bx, word [BytesPerSector]
loop read_sectors
ret
start:
cli
mov ax, 0x07c0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0
mov ss, ax
mov sp, 0xffff
sti
load_root:
xor cx, cx
xor dx, dx
mov ax, 32
mul word [RootDirEntries]
div word [BytesPerSector]
xchg ax, cx
mov al, byte [NumberOfFats]
mul word [SectorsPerFat]
add ax, word [ReservedForBoot]
mov word [datasector], ax
add word [datasector], cx
mov bx, 0x0200
call read_sectors
mov cx, word [RootDirEntries]
mov di, 0x0200
.loop:
push cx
mov cx, 11
mov si, kernel
push di
rep cmpsb
pop di
je load_fat
pop cx
add di, 32
loop .loop
jmp failure
load_fat:
mov dx, word [di+0x001a]
mov word [cluster], dx
xor ax, ax
mov al, byte [NumberOfFats]
mul word [SectorsPerFat]
mov cx, ax
mov ax, word [ReservedForBoot]
mov bx, 0x0200
call read_sectors
mov ax, 0x7e00
mov es, ax
mov bx, 0x0000
load_kernel:
mov ax, word [cluster]
call chs_lba
xor cx, cx
mov cl, byte [SectorsPerCluster]
call read_sectors
mov ax, word [cluster]
mov cx, ax
mov dx, ax
shr dx, 1
add cx, dx
mov bx, 0x0200
add bx, cx
mov dx, word [bx]
test ax, 1
jnz .odd
.even:
and dx, 0000111111111111b
jmp .done
.odd:
shr dx, 4
.done:
mov word [cluster], dx
cmp dx, 0x0ff0
jb load_kernel
pusha
mov di, 0x7e00
xor ax, ax
mov cx, 512
rep stosb
execute_kernel:
;push word 0x7e00
;push word 0x0000
;retf
jmp 0x7e00:0x0000
failure:
mov si, msg
call print
mov ah, 0
int 0x16
int 0x19
absoluteSector db 0
absoluteHead db 0
absoluteTrack db 0
datasector dw 0
cluster dw 0
kernel db 'KERNEL SYS'
msg db 'MISSING KERNEL. Press any key to reboot...', 0xA, 0xD, 0
times 510-($-$$) db 0
dw 0xAA55
您知道实模式寻址使用 16*segment+offset
作为物理地址,对吗?您在 0x7E00:0000
处加载代码,因此是物理地址 0x7E000
(注意 3 个零)。但是您的内核需要地址 0x7E00
(注意 2 个零)。
你的代码是双重错误的。首先,您实际上跳转到了偏移量 0
,因此您应该使用 org 0
(这是默认设置)。其次,必须为实模式段调整物理地址,即jmp dword 0x8:b32+0x7e000
。这将修复当前代码,但 32 位部分将再次使用错误的 org
。
你正在让自己的生活不必要地复杂化。通常的最佳做法是将代码加载到前 64k 内的地址中,您可以在其中使用段 0 和 16 位偏移量直接映射到实内存和平面保护模式中的物理内存。因此,我建议加载到 0:0x8000
.