Problema para cambiar al modo v8086 desde el modo protegido de 32 bits configurando EFLAGS.VM en 1
Estoy en modo protegido de 32 bits ejecutándose en el nivel de privilegio actual (CPL = 0). Estoy tratando de ingresar al modo v8086 estableciendo el indicador EFLAGS.VM (Bit 17) en 1 (y IOPL en 0) y haciendo un FAR JMP en mi código de modo real de 16 bits. Recibo las banderas actuales usandoPUSHF
; establezca EFLAGS.VM (bit 17) en 1; establezca EFLAGS.IOPL (bit 22 y bit 23) en 0; establecer los nuevos EFLAGS conPOPF
. El código para esto se ve así:
bits 32
cli
[snip]
pushf ; Get current EFLAGS
pop eax
or eax, 1<<EFLAGS_VM_BIT ; Set VM flag to enter v8086 mode
and eax, ~(3<<EFLAGS_IOPL_BITS)
; Set IOPL to 0
; IF flag already 0 because of earlier CLI
push eax
popf ; Reload new flags
jmp CODE32_SEL:v86_mode_entry
; Far JMP to v8086 entry point
; v8086 code entry point
bits 16
v86_mode_entry:
hlt ; Halt should double fault
[snip]
Para estas pruebas, estoy ejecutando deliberadamente con:
Se interrumpe en CPL = 0 todo el tiempo. Se interrumpe cuando se ejecuta en modo v8086.No tengo un IDT.No tengo un TSS ya que no estoy haciendo la transición entre niveles de privilegio a través de interrupciones, puertas y excepciones.Para probar si he ingresado al modo v8086, hago unHLT
instrucción. Como no tengo mecanismos de interrupción, espero que ocurra una doble falla. Loshlt
parece ejecutarse correctamente y el sistema se encuentra allí. En BOCHs cuando llego a lahlt
Noté que las banderas son:
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
l indicador @ EFLAGS.VM está marcado como apagado (0) ya que aparece comovm
y noVM
. Esto no es lo que esperaba.
hlt
fallas dobles?Es posible ingresar al modo v8086 mientras está en modo largo de 64 bits o compatibilidad de 32 bits (modo largo)?n ejemplo mínimo verificable completo de este código es un gestor de arranque que ingresa al modo protegido y realiza las tareas descritas anteriormente:
VIDEO_TEXT_ADDR EQU 0xb8000 ; Hard code beginning of text video memory
ATTR_BWHITE_ON_GREEN EQU 0x2f ; Bright white on black attribute
ATTR_BWHITE_ON_MAGENTA EQU 0x5f ; Bright White on magenta attribute
PM_MODE_STACK EQU 0x80000 ; Protected mode stack below EBDA
EFLAGS_VM_BIT EQU 17 ; EFLAGS VM bit
EFLAGS_IOPL_BITS EQU 12 ; EFLAGS IOPL bits (bit 12 and bit 13)
; Macro to build a GDT descriptor entry
%define MAKE_GDT_DESC(base, limit, access, flags) \
(((base & 0x00FFFFFF) << 16) | \
((base & 0xFF000000) << 32) | \
(limit & 0x0000FFFF) | \
((limit & 0x00FF0000) << 32) | \
((access & 0xFF) << 40) | \
((flags & 0x0F) << 52))
bits 16
ORG 0x7c00
; Include a BPB (1.44MB floppy with FAT12) to be more compatible with USB floppy media
; %include "bpb.inc"
boot_start:
xor ax, ax ; DS=SS=ES=0
mov ds, ax
mov ss, ax ; Stack at 0x0000:0x7c00
mov sp, 0x7c00
cld ; Set string instructions to use forward movement
; Fast method of enabling A20 may not work on all x86 BIOSes
; It is good enough for emulators and most modern BIOSes
; See: https://wiki.osdev.org/A20_Line
cli ; Disable interrupts for rest of code as we don't
; want A20 code to be interrupted. In protected mode
; we have no IDT so any interrupt that does occur will
; double fault and reboot.
in al, 0x92
or al, 2
out 0x92, al ; Enable A20 using Fast Method
lgdt [gdtr] ; Load our GDT
mov eax, cr0
or eax, 1
mov cr0, eax ; Set protected mode flag
jmp CODE32_SEL:start32 ; FAR JMP to set CS
; v8086 code entry point
v86_mode_entry:
hlt ; Halt
; 32-bit protected mode entry point
bits 32
start32:
mov ax, DATA32_SEL ; Setup the segment registers with data selector
mov ds, ax
mov es, ax
mov ss, ax
mov esp, PM_MODE_STACK ; Set protected mode stack pointer
mov fs, ax ; Not currently using FS and GS
mov gs, ax
mov ah, ATTR_BWHITE_ON_GREEN; Attribute to print with
mov al, ah ; Attribute to clear last line when scrolling
mov esi, in_pm_msg ; Print message that we are in protected mode
call print_string_pm
pushf ; Get current EFLAGS
pop eax
or eax, 1<<EFLAGS_VM_BIT ; Set VM flag to enter v8086 mode
and eax, ~(3<<EFLAGS_IOPL_BITS)
; Set IOPL to 0
; IF flag already 0 because of earlier CLI
push eax
popf ; Reload new flags
jmp CODE32_SEL:v86_mode_entry
; Far JMP to v8086 entry point
; Function: print_string_pm
; Display a string to the console on display page 0 in protected mode.
; Very basic. Doesn't update hardware cursor, doesn't handle scrolling,
; LF, CR, TAB.
;
; Inputs: ESI = Offset of address to print
; AH = Attribute of string to print
; Clobbers: None
; Returns: None
print_string_pm:
push edi
push esi
push eax
mov edi, [vidmem_ptr] ; Start from video address stored at vidmem_ptr
jmp .getchar
.outchar:
stosw ; Output character to video display
.getchar:
lodsb ; Load next character from string
test al, al ; Is character NUL?
jne .outchar ; If not, go back and output character
mov [vidmem_ptr], edi ; Update global video pointer
pop eax
pop esi
pop edi
ret
align 4
vidmem_ptr: dd VIDEO_TEXT_ADDR ; Start console output in upper left of display
in_pm_msg:
db "In 32-bit protected mode!", 0
align 4
gdt_start:
dq MAKE_GDT_DESC(0, 0, 0, 0) ; null descriptor
gdt32_code:
dq MAKE_GDT_DESC(0, 0x00ffffff, 10011010b, 11001111b)
; 32-bit code, 4kb gran, limit 0xffffffff bytes, base=0
gdt32_data:
dq MAKE_GDT_DESC(0, 0x00ffffff, 10010010b, 11001111b)
; 32-bit data, 4kb gran, limit 0xffffffff bytes, base=0
end_of_gdt:
gdtr:
dw end_of_gdt - gdt_start - 1
; limit (Size of GDT - 1)
dd gdt_start ; base of GDT
CODE32_SEL equ gdt32_code - gdt_start
DATA32_SEL equ gdt32_data - gdt_start
; Pad boot sector to 510 bytes and add 2 byte boot signature
TIMES 510-($-$) db 0
dw 0xaa55
El gestor de arranque se puede generar con:
nasm -f bin v86.asm -o v86.bin
Se puede ejecutar en QEMU con:
qemu-system-i386 -fda v86.bin