3 ; Test program: Protected mode via VCPI
4 ; (C) 2010-2012 Jonathan Campbell.
5 ; Hackipedia DOS library.
7 ; This code is licensed under the LGPL.
8 ; <insert LGPL legal text here>
11 ; switching the CPU into 386 16-bit protected mode (and back) using VCPI
12 bits 16 ; 16-bit real mode
13 org 0x100 ; MS-DOS .COM style
15 ; assume ES == DS and SS == DS and DS == CS
34 mov dx,str_cpu_not_386
35 jmp exit2dos_with_message
38 ; ===== CHECK FOR VIRTUAL 8086 MODE. IN THIS CASE WE WANT IT, IT MEANS VCPI IS PRESENT
39 smsw ax ; 386 or higher: If we're in real mode
40 test al,1 ; and bit 0 is already set, we're in virtual 8086
41 jnz isnt_realmode ; and our switch to prot mode will cause problems.
42 mov dx,str_cpu_need_v86_mode
43 jmp exit2dos_with_message
46 ; choose memory location for PAGE0, PAGEDIR. they must be 4K aligned
57 mov dword [PAGEDIR],edi
59 ; ===== CHECK FOR VALID INT 67h
66 mov dx,str_cpu_need_int67
67 jmp exit2dos_with_message
69 ; ===== CHECK FOR VCPI
70 int67_valid: mov ax,0xDE00
74 mov dx,str_cpu_need_vcpi
75 jmp exit2dos_with_message
77 ; fill in the page table, get VCPI selectors, and entry point
78 vcpi_valid: mov ax,0xDE01
82 mov esi,GDT + VCPI0_SEL
86 mov dx,str_cpu_need_vcpi_info
87 jmp exit2dos_with_message
88 vcpi_valid2: mov dword [vcpi_entry],ebx
104 ; ===== BUILD THE GLOBAL DESCRIPTOR TABLE AND GDTR REGISTER
106 mov word [MY_SEGMENT],ax
107 mov word [MY_SEGMENT+2],0
110 shl ax,4 ; BX:AX = 32-bit physical addr of our segment
111 mov word [MY_PHYS_BASE],ax
112 mov word [MY_PHYS_BASE+2],bx
115 adc bx,0 ; BX:AX += offset of GDT
117 mov word [GDTR],MAX_SEL - 1
119 mov word [GDTR+4],bx ; GDTR: limit MAX_SEL-1 base=physical mem addr of GDT
121 mov ax,word [MY_PHYS_BASE]
122 mov bx,word [MY_PHYS_BASE+2]
146 dec ax ; 0x0000 - 1 = 0xFFFF
148 mov ax,[MY_PHYS_BASE]
150 mov al,[MY_PHYS_BASE+2]
152 stosw ; BASE[23:16] access byte=executable readable
154 mov ah,[MY_PHYS_BASE+3] ; LIMIT[19:16] flags=0 BASE[31:24]
160 mov ax,[MY_PHYS_BASE]
162 mov al,[MY_PHYS_BASE+2]
164 stosw ; BASE[23:16] access byte=data writeable
166 mov ah,[MY_PHYS_BASE+3] ; LIMIT[19:16] flags=0 BASE[31:24]
168 ; Data selector (video)
174 mov al,0x0B ; BASE=0xB8000
176 stosw ; BASE[23:16] access byte=data writeable
178 mov ah,[MY_PHYS_BASE+3] ; LIMIT[19:16] flags=0 BASE[31:24]
180 ; Code selector (32-bit)
181 dec ax ; 0x0000 - 1 = 0xFFFF
183 mov ax,[MY_PHYS_BASE]
185 mov al,[MY_PHYS_BASE+2]
187 stosw ; BASE[23:16] access byte=executable readable
189 mov ah,[MY_PHYS_BASE+3] ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
191 ; Data selector (32-bit)
195 mov ax,[MY_PHYS_BASE]
197 mov al,[MY_PHYS_BASE+2]
199 stosw ; BASE[23:16] access byte=data writeable
201 mov ah,[MY_PHYS_BASE+3] ; LIMIT[19:16] flags=granular 32-bit BASE[31:24]
209 ; TSS selector (TSS_SEL)
210 mov ebx,[MY_PHYS_BASE]
219 stosw ; BASE[23:16] access byte=data writeable non-busy TSS type 9
221 mov ah,bh ; LIMIT[19:16] flags=0 BASE[31:24]
223 ; TSS selector (TSS_VM86_SEL)
224 mov ebx,[MY_PHYS_BASE]
233 stosw ; BASE[23:16] access byte=data writeable non-busy TSS type 9
235 mov ah,bh ; LIMIT[19:16] flags=0 BASE[31:24]
238 ; prepare page directory
244 mov edx,[MY_PHYS_BASE]
260 mov edx,[MY_PHYS_BASE]
264 mov dword [di+0],eax ; ESI+0 = CR3 register = (SEG<<4)+PAGE0
267 mov dword [di+4],eax ; ESI+4 = GDTR
270 mov dword [di+8],eax ; ESI+8 = IDTR
272 mov word [di+0xC],0 ; ESI+C = LDTR
273 mov word [di+0xE],TSS2_SEL ; ESI+E = TR
274 mov dword [di+0x10],prot16_entry ; ESI+12 = prot16_entry
275 mov dword [di+0x14],CODE_SEL ; ESI+10 = CS
277 ; =============== JUMP INTO PROTECTED MODE USING VCPI
279 add esi,[MY_PHYS_BASE] ; ESI = *LINEAR* address (not DS:ESI!)
289 ; draw directly onto VGA alphanumeric RAM at 0xB8000
296 vdraw1: lodsb ; AL = DS:SI++
300 stosw ; ES:DI = AX, DI += 2
304 ; now, jump into 32-bit protected mode
305 jmp CODE32_SEL:prot32_entry
307 prot32_entry: mov ax,DATA32_SEL
313 ; draw directly onto VGA alphanumeric RAM at 0xB8000
316 mov edi,0xB8000+(80*2)
317 sub edi,[MY_PHYS_BASE]
318 vdraw321: lodsb ; AL = DS:SI++
322 stosw ; ES:DI = AX, DI += 2
326 ; jump 32-bit to 16-bit
327 jmp CODE_SEL:prot32_to_prot
329 prot32_to_prot: mov ax,DATA_SEL
334 ; =============== JUMP OUT OF PROTECTED MODE USING VCPI
338 push eax ; SS:ESP+0x28 GS
339 push eax ; SS:ESP+0x24 FS
340 push eax ; SS:ESP+0x20 DS
341 push eax ; SS:ESP+0x1C ES
342 push eax ; SS:ESP+0x18 SS
343 push ebx ; SS:ESP+0x14 ESP
344 pushfd ; SS:ESP+0x10 EFLAGS
345 push eax ; SS:ESP+0x0C CS
346 push dword realmode_entry ; SS:ESP+0x08 EIP
347 push dword VCPI0_SEL ; SS:ESP+0x04
348 push dword [vcpi_entry] ; SS:ESP+0x00
349 mov eax,0xDE0C ; 0xDE0C = switch to v86 mode
350 jmp far dword [esp] ; simulate a CALL FAR (SS: override implied)
352 ; ====== PROVE WE MADE IT TO REAL MODE
353 realmode_entry: mov si,vdraw2_msg
357 vdraw2: lodsb ; AL = DS:SI++
361 stosw ; ES:DI = AX, DI += 2
371 ; ===== EXIT TO DOS WITH ERROR MESSAGE DS:DX
372 exit2dos_with_message:
376 exit2dos: mov ax,4C00h
379 ; 8086 test: EFLAGS will always have bits 12-15 set
390 ; 286 test: EFLAGS will always have bits 12-15 clear
401 cpu_is_386_not: mov ax,1
406 str_cpu_not_386: db "386 or higher required$"
407 str_cpu_need_v86_mode: db "Virtual 8086 mode required (VCPI)$"
408 str_cpu_need_int67:db "INT 67h service required$"
409 str_cpu_need_vcpi:db "VCPI server required$"
410 str_cpu_need_vcpi_info:db "VCPI server failed to return info$"
411 vdraw2_msg: db "This message was drawn on screen back from real mode!",0
412 vdraw_msg: db "This message was drawn on screen from 386 16-bit protected mode!",0
413 vdraw32_msg: db "This message was drawn on screen from 386 32-bit protected mode!",0
424 VCPI_SETUP: resb 0x80