Следующий минимальный полный пример демонстрирует эту концепцию. В этом примере IOPB настроен так, чтобы разрешить порт доступу к первым портам 0x400 и запретить его для остальных:

ожусь в 32-битном защищенном режиме, работающем с текущим уровнем привилегий (CPL = 0). Я пытаюсь войти в режим v8086, устанавливая флаг EFLAGS.VM (бит 17) в 1 (и IOPL в 0) и выполняю FAR JMP для моего 16-битного кода реального режима. Я получаю текущие флаги используяPUSHF; установите EFLAGS.VM (бит 17) в 1; установите EFLAGS.IOPL (бит 22 и бит 23) в 0; установить новые EFLAGS сPOPF, Код для этого выглядит так:

    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]

Для этих тестов я намеренно работаю с:

Прерывает при CPL = 0 все время.Прерывается при работе в режиме v8086.У меня нет IDT.У меня нет TSS, поскольку я не перехожу между уровнями привилегий через прерывания, ворота и исключения.

Чтобы проверить, вошел ли я в режим v8086, я делаюHLT инструкция. Поскольку у меня нет механизмов прерывания, я ожидаю двойной ошибки.hlt кажется, работает правильно, и система сидит там. В BOCHs, когда я достигаюhlt Я заметил флаги:

eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf

Флаг EFLAGS.VM помечен как выключенный (0), так как он указан какvm и нетVM, Это не то, что я ожидал.

Вопросов:Что не так с моим кодом, и как его можно исправить, чтобы войти в режим v8086 иhlt двойные ошибки?Можно ли войти в режим v8086 в 64-битном длинном режиме или в 32-битной совместимости (длинном режиме)?

Минимальным полным проверяемым примером этого кода является загрузчик, который входит в защищенный режим и выполняет задачи, описанные выше:

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

Загрузчик может быть сгенерирован с помощью:

nasm -f bin v86.asm -o v86.bin

Его можно запустить в QEMU с помощью:

qemu-system-i386 -fda v86.bin

Ответы на вопрос(2)

Ваш ответ на вопрос