awik wrote:Yes, but wherever the flag does get set, it's not done by my code.
I'm very curious about your code. I feel like there is something fishy going on here and it is highly unusual the NT flag would be set in EFLAGS under most conditions unless some kind of task gates were used OR there was a bug that reloaded the EFLAGS with the wrong value. Do you use someone else's bootloader? Multiboot/Grub? What environment do you run from? An emulator like QEMU/BOCHS? Real hardware?
Is it possible to post all of your code? I ask this because it is unusual enough that NT is set that suggests there *might( be a bug somewhere else. You of course can put your own values into EFLAGS (as I suggested in my updated comment earlier) with NT turned off, but why it was on to begin with is mysterious.
Here is some code that enters protected mode and then v8086 mode (with IOPL=3) in a legacy bootloader (note: the A20 switch isn't robust so might not work on all real hardware, and lacks a BPB). It will prints something to the display. You should be able to run it on BOCHS/QEMU and most real hardware emulating USB FDD (floppy):
Code: Select all
; Assemble with
; nasm -f bin boot.asm -o boot.bin
VIDEO_TEXT_ADDR EQU 0xb8000 ; Hard code beginning of text video memory
ATTR_BWHITE_ON_GREEN EQU 0x2f ; Bright white on green attribute
ATTR_BWHITE_ON_MAGENTA EQU 0x5f ; Bright White on magenta attribute
PM_MODE_STACK EQU 0x80000 ; Protected mode stack below EBDA
V86_STACK_SEG EQU 0x0000 ; v8086 stack SS
V86_STACK_OFS EQU 0x0000 ; v8086 stack SP
V86_CS_SEG EQU 0x0000 ; v8086 code segment CS
EFLAGS_VM_BIT EQU 17 ; EFLAGS VM bit
EFLAGS_BIT1 EQU 1 ; EFLAGS bit 1 (reserved, always 1)
EFLAGS_IF_BIT EQU 9 ; EFLAGS IF bit
EFLAGS_IOPL_BITS EQU 12 ; EFLAGS IOPL bits 12-13
IOPL EQU 3 ; v8086 IOPL = 3
TSS_IO_BITMAP_SIZE EQU 256/8 ; IO Bitmap for 256 IO ports
; Size 0 disables IO port bitmap (no permission)
; Macro to build a GDT descriptor entry
%define MAKE_GDT_DESC(base, limit, access, flags) \
(((base & 0x00FFFFFF) << 16) | \
((base & 0xFF000000) << 32) | \
(limit & 0x0000FFFF) | \
((limit & 0x000F0000) << 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
global bpb_disk_info
jmp short boot_continue
nop
bpb_disk_info:
; Dos 4.0 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
boot_continue:
cli ; Disable interrupts for this demo
xor ax, ax ; DS=SS=ES=0
mov ds, ax
mov es, 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
in al, 0x92
or al, 2
out 0x92, al ; Enable A20 using Fast Method
lgdt [gdt.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:
sub dword [vidmem_ptr], VIDEO_TEXT_ADDR
; Adjust video pointer to be relative to beginning of
; segment 0xb800
mov si, in_v86_msg ; Print in v86 message
mov ah, ATTR_BWHITE_ON_MAGENTA
; Attribute to print with
call print_string_rm_nobios
.endloop:
jmp $ ; Infinite loop since we didn't code a solution to exit VM
; Function: print_string_rm_nobios
; Display a string to the console on display page 0 in real/v8086 mode
; without using the BIOS. We don't have a proper v8086 monitor so can't
; use BIOS to display.
;
; Very basic. Doesn't update hardware cursor, doesn't handle scrolling,
; LF, CR, TAB.
;
; Inputs: SI = Offset of address to print
; AH = Attribute of string to print
; Clobbers: None
; Returns: None
print_string_rm_nobios:
push di
push si
push ax
push es
mov di, VIDEO_TEXT_ADDR>>4 ; ES=0xb800 (text video mode segment)
mov es, di
mov di, [vidmem_ptr] ; Start from video address stored at vidmem_ptr
jmp .getchar
.outchar:
stosw ; Output character to display
.getchar:
lodsb ; Load next character from string
test al, al ; Is character NUL?
jne .outchar ; If not, go output character
mov [vidmem_ptr], di ; Update global video pointer
pop es
pop ax
pop si
pop di
ret
; 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
mov ecx, tss_struct_size ; Zero out entire TSS structure
mov edi, tss0
xor eax, eax
rep stosb
mov eax, tss0
; Set iomap_base in tss with the offset of the iomap relative to beginning of the tss
mov word [eax + tss_struct.iomap_base], tss_struct.iomap
%if TSS_IO_BITMAP_SIZE > 0
mov byte [eax + tss_struct.iomap_pad], 0xff
%endif
; Update TSS entry in GDT with base of the TSS (tss0)
mov [gdt.tss32_0 + 2], ax
shr eax, 16
mov [gdt.tss32_0 + 4], al
mov [gdt.tss32_0 + 7], ah
mov eax, TSS32_0_SEL
ltr ax ; Load default TSS (used for exceptions, interrupts, etc)
xor ebx, ebx ; EBX=0
push ebx ; Real mode GS=0
push ebx ; Real mode FS=0
push ebx ; Real mode DS=0
push ebx ; Real mode ES=0
push V86_STACK_SEG
push V86_STACK_OFS ; v8086 stack SS:SP (grows down from SS:SP)
push dword 1<<EFLAGS_VM_BIT | 1<<EFLAGS_BIT1 | IOPL<<EFLAGS_IOPL_BITS
; Set VM Bit, IF bit is off, DF=0(forward direction),
; IOPL=0, Reserved bit (bit 1) always 1. Everything
; else 0. These flags will be loaded in the v8086 mode
; during the IRET. We don't want interrupts enabled
; because we have no v86 monitor via protected mode
; GPF handler
push V86_CS_SEG ; Real Mode CS (segment)
push v86_mode_entry ; Entry point (offset)
iret ; Transfer control to v8086 mode and our real mode code
; 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
in_v86_msg:
db "In v8086 mode!", 0
align 4
gdt:
CODE32_SEL equ .code32 - .start
DATA32_SEL equ .data32 - .start
TSS32_0_SEL equ .tss32_0 - .start
.start:
.null:
dq MAKE_GDT_DESC(0, 0, 0, 0) ; null descriptor
.code32:
dq MAKE_GDT_DESC(0, 0x000fffff, 10011010b, 1100b)
; 32-bit code, 4kb gran, limit 0xffffffff bytes, base=0
.data32:
dq MAKE_GDT_DESC(0, 0x000fffff, 10010010b, 1100b)
; 32-bit data, 4kb gran, limit 0xffffffff bytes, base=0
.tss32_0:
dq MAKE_GDT_DESC(0, tss_struct_size-1, 10001001b, 0000b)
; 32-bit TSS, 1b gran, available, IOPL=0
.end:
.gdtr:
dw .end - .start - 1
; limit (Size of GDT - 1)
dd .start ; base of GDT
; Pad boot sector to 510 bytes and add 2 byte boot signature
TIMES 510-($-$$) db 0
dw 0xaa55
struc tss_struct
.back_link: resd 1
.esp0: resd 1 ; Kernel stack pointer used on ring transitions
.ss0: resd 1 ; Kernel stack segment used on ring transitions
.esp1: resd 1
.ss1: resd 1
.esp2: resd 1
.ss2: resd 1
.cr3: resd 1
.eip: resd 1
.eflags: resd 1
.eax: resd 1
.ecx: resd 1
.edx: resd 1
.ebx: resd 1
.esp: resd 1
.ebp: resd 1
.esi: resd 1
.edi: resd 1
.es: resd 1
.cs: resd 1
.ss: resd 1
.ds: resd 1
.fs: resd 1
.gs: resd 1
.ldt: resd 1
.trap: resw 1
.iomap_base:resw 1 ; IOPB offset
;.cetssp: resd 1 ; Need this if CET is enabled
; Insert any kernel defined task instance data here
; ...
; If using VME (Virtual Mode extensions) there need to bean additional 32 bytes
; available immediately preceding iomap. If using VME uncomment next 2 lines
;.vmeintmap: ; If VME enabled uncomment this line and the next
; resb 32 ; 32*8 bits = 256 bits (one bit for each interrupt)
.iomap: resb TSS_IO_BITMAP_SIZE ; IO bitmap (IOPB) size 8192 (8*8192=65536) representing
; all ports. An IO bitmap size of 0 would fault all IO
; port access if IOPL < CPL (CPL=3 with v8086)
%if TSS_IO_BITMAP_SIZE > 0
.iomap_pad: resb 1 ; Padding byte that has to be filled with 0xff
; To deal with issues on some CPUs when using an IOPB
%endif
endstruc
section .bss
tss0: resb tss_struct_size
If this code fails to get into v8086 mode because NT was set previously then it would have to have been a result of the BIOS leaving it in that state.