Use BIOS funcions from long mode
Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Use BIOS funcions from long mode
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.
Not so ugly as it first seems :-) Hope that would be useful.
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-----
- Combuster
- Member
Member - Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 11⁄2m distance
- Contact:
Re: Use BIOS funcions from long mode
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
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.
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.
"God! Not Unix" - Richard Stallman
Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed
Website: venom Dev
OS project: venom OS
Hexadecimal Editor: hexed
- Combuster
- Member
Member - Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 11⁄2m distance
- Contact:
Re: Use BIOS funcions from long mode
In theory, it doesn't work, because you can't easily deal with these cases: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.
- 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
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.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).
Last edited by turdus on Wed Feb 09, 2011 12:37 pm, edited 1 time in total.