Page 1 of 1

Use BIOS funcions from long mode

Posted: Tue Feb 08, 2011 2:55 pm
by turdus
Before any of you write "no, you cannot do that", I have a working solution I want to share.
Prerequirements:
1. I assume you have set up long mode properly
2. Your kernel is _not_ in the first 64k, so you have to alter CS for real mode. This example code use 1000h, but change it to your needs (kinda homework).
3. The original IVT is untouched

The code uses fasm syntax.

Code: Select all

;this code is placed somewhere after 10000h
;-----we're in LONG MODE-----
		mov			 qword [.stckptr], rsp	;first of all save stack
		sgdt			[.gdtv64]					;save your gdt pointer
		lgdt			[.gdtv16]					;load a new one
		sidt			[.idt64]					 ;save your idt pointer
		lidt			[.idt16]					 ;load real mode idt
		;far jump in long mode is not possible, do a trick
		push			DESC_REAL
		push			@f-10000h				   ;this is CS*10h, modify if needed!
		retfq
.stckptr:
		dq			  0
		align		  16
.gdtv64:
		dw			  0
		dq			  0
		align		  16
.gdtv16:
		dw			  .gdtend-.gdt-1
		dd			  .gdt,0
		align		  16
.gdt:
		dd			  0,0					       ;null descriptor
DESC_DATA=8								         ;descriptor in YOUR GDT (modify)
DESC_LONG=$-.gdt
		dd			  00000000h,00209800h	   ;64 bit long mode cs
DESC_REAL=$-.gdt
		dd			  0000FFFFh,00009801h	   ;16 bit real mode cs (modify base if needed!)
.gdtend:
		align		  16
.idt64:
		dw			  0
		dq			  0
		align		  16
.idt16:
		dw			  3FFh
		dq			  0
		USE16
;-----we're in COMPATIBLITY MODE-----
		;disable paging and protmode at once
@@:	mov			 eax, cr0
		and			 eax, 7FFFFFFEh	
		mov			 cr0, eax
		;set up real mode segment registers and stack
		mov			 esp, realmode_stack_top		    ;modify it to your needs!
		xor			 ax, ax
		mov			 ds, ax
		mov			 es, ax
		mov			 fs, ax
		mov			 gs, ax
		mov			 ss, ax
		;convert long mode rip to real mode cs:ip
		;jmp CS:(longmode address)-CS*10h
		jmp			 1000h:@f-10000h			         ;modify if needed!
;-----we're in REAL MODE-----
@@:	;***********call some BIOS interrupt here**********
		mov			 ax, 3
		int			 10h

		;switch back to long mode
		mov			 eax, cr0
		or           eax, 80000001h
		mov          cr0, eax					          ;enable protmode and paging
		;jmp		   DESC_LONG:@f
		db			  66h
		db			  0EAh
		dd			  @f
		dw			  DESC_LONG
		USE64
;-----we're in COMPATIBILITY MODE-----
@@:	lgdt			[cs:.gdtv64]				        ;restore gdt
		mov          ax, DESC_DATA				       ;read YOUR DATA descriptor to selectors
		mov          ds, ax
		mov          es, ax
		mov          fs, ax
		mov          gs, ax
		mov          ss, ax
		lidt			[.idt64]					         ;restore idt
		mov			 rsp, qword [.stckptr]			  ;restore stack
		;must be a non rip-relative jump
		mov			 rax, @f
		jmp			 rax
@@:
;-----we're in LONG MODE again-----
Not so ugly as it first seems :-) Hope that would be useful.

Re: Use BIOS funcions from long mode

Posted: Wed Feb 09, 2011 12:13 am
by Combuster
You're technically not calling the BIOS from long mode, but from real mode. That obviously comes with all the problems of temporarily disabling your OS that come with it.

Re: Use BIOS funcions from long mode

Posted: Wed Feb 09, 2011 1:44 am
by b.zaar
I'm not playing in long mode yet, but I was thinking could you fake a vm86 mode using 16bit pmode segments. I think 16 bit selectors are legal in long mode (compatibility mode). This is just a theory and I haven't tried it so it could all be BS.

using ldt entries cs = 0x00 ds = 0x08 es = 0x10 fs = 0x18 gs = 0x20 ss = 0x28
(for gdt entries just start with cs = 0x08 instead)
whenever a real mode program (BIOS interrupt) program faults by using an invalid segment then set the allocated selector to the base with a limit of 64K and reload it back into the selector. Because the real mode IVT goes up to 0x0400 most programs wont request anything below this as a segment except maybe 0x00 which will trigger a fault because to use selector 0x00 it would need to be loaded as 0x07 for ring3/ldt flags.

This is basically a normal vm86 monitor to handle protected instruction but also it sets the selectors whenever an invalid read/write happens. You can still set the v86 bit in the eflags for long mode to detect whether you need to set selectors.

Just curious if this is a viable option.

Re: Use BIOS funcions from long mode

Posted: Wed Feb 09, 2011 2:26 am
by Combuster
b.zaar wrote:I'm not playing in long mode yet, but I was thinking could you fake a vm86 mode using 16bit pmode segments. I think 16 bit selectors are legal in long mode (compatibility mode). This is just a theory and I haven't tried it so it could all be BS.

using ldt entries cs = 0x00 ds = 0x08 es = 0x10 fs = 0x18 gs = 0x20 ss = 0x28
(for gdt entries just start with cs = 0x08 instead)
whenever a real mode program (BIOS interrupt) program faults by using an invalid segment then set the allocated selector to the base with a limit of 64K and reload it back into the selector. Because the real mode IVT goes up to 0x0400 most programs wont request anything below this as a segment except maybe 0x00 which will trigger a fault because to use selector 0x00 it would need to be loaded as 0x07 for ring3/ldt flags.

This is basically a normal vm86 monitor to handle protected instruction but also it sets the selectors whenever an invalid read/write happens. You can still set the v86 bit in the eflags for long mode to detect whether you need to set selectors.

Just curious if this is a viable option.
In theory, it doesn't work, because you can't easily deal with these cases:
- Real mode applications using 0 for a segment register (all accesses will fail)
- using CS=DS (or any other form of data-code aliasing) requires that the same entry contains both a code and data selector, which might have you end up with unwritable segments for code descriptors, or faults for data descriptors.
- using segment and segment +1 (so they alias the same table access), which will result in wrong addresses used
- using the same segment value as one used for your kernel.

And you can't modify a segment register upon fault if the process uses some form of paragraph arithmetic (even simple instances like push ds; pop es;) because you can't check if a segment is an actual location, or some operation based on whatever value you previously replaced.

You'll probably find #1 and #2 very often in production code.


[EDIT] Brendan added the quote, so that turdus doesn't make the mistake of thinking that Combuster is saying his "switch to real mode" idea won't work.[/EDIT]

Re: Use BIOS funcions from long mode

Posted: Wed Feb 09, 2011 12:25 pm
by turdus
b.zaar wrote:I'm not playing in long mode yet, but I was thinking could you fake a vm86 mode using 16bit pmode segments. I think 16 bit selectors are legal in long mode (compatibility mode).
Usually an OS won't call any BIOS calls, it's very-very rare. Maybe at boottime and switching video modes, that's all, so it not worth the candle imho.