BIOS calls for VESA functionalities
BIOS calls for VESA functionalities
Hi.
I am new in this Forum.
On my way to develop my OS and using VESA for it, I got an issue. It is now in protected mode and I want to call VESA functionalities. I used the source code provided by Napalm at Rohitab (http://www.rohitab.com/discuss/user/3860-napalm/).
Some functionalities work perfectly, but one of them not.
I have a struct called VBE_INFO which holds the vbe info such as vendor, version, etc. Then I supplied reg.ax with 0xF400, regs.es with SEGMENT(&vbe_info) which passes the segment of the pointer and reg.di with OFFSET(&vbe_info) which passes the offset of the 32-bit pointer.
After calling int32 function with 0x10 and ® as the arguments, I checked the return value in ax and indeed it is 0x00F4. But the vbe_info did not updated by the BIOS. For instance, vbe_info.version is 0. It made me thought perhaps there is a bug in the int32 calling function.
Is there anybody who can help me with that? Maybe others had the same problem before.
Thanks.
I am new in this Forum.
On my way to develop my OS and using VESA for it, I got an issue. It is now in protected mode and I want to call VESA functionalities. I used the source code provided by Napalm at Rohitab (http://www.rohitab.com/discuss/user/3860-napalm/).
Some functionalities work perfectly, but one of them not.
I have a struct called VBE_INFO which holds the vbe info such as vendor, version, etc. Then I supplied reg.ax with 0xF400, regs.es with SEGMENT(&vbe_info) which passes the segment of the pointer and reg.di with OFFSET(&vbe_info) which passes the offset of the 32-bit pointer.
After calling int32 function with 0x10 and ® as the arguments, I checked the return value in ax and indeed it is 0x00F4. But the vbe_info did not updated by the BIOS. For instance, vbe_info.version is 0. It made me thought perhaps there is a bug in the int32 calling function.
Is there anybody who can help me with that? Maybe others had the same problem before.
Thanks.
-
- Member
- Posts: 798
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: BIOS calls for VESA functionalities
It would probably help to see your actual code.Do you have your OS on Gitlab or Github (or similar service)? Sounds like the memory address of your vbe_info (Segment and Offset) structure that you told Int 10h/04f00h to fill in. Was this a typo?
Did you mean 0x4F00?Then I supplied reg.ax with 0xF400,
Re: BIOS calls for VESA functionalities
Hi, and welcome!
You should also initialize the buffer with a magic value. Here's how I do it:
Cheers,
bzt
You should also initialize the buffer with a magic value. Here's how I do it:
Code: Select all
xor ax, ax
mov es, ax
mov ax, 4f00h
mov di, 0A000h
mov dword [di], 'VBE2'
int 10h
cmp ax, 004fh
je @f
mov si, novbe
jmp die
@@:
bzt
Re: BIOS calls for VESA functionalities
Does this mean you are executing an INT 10h instruction while in protected mode?iman wrote:It is now in protected mode and I want to call VESA functionalities.
As MichaelPetch asked, is this a typo? It should be 0x4F00.iman wrote:Then I supplied reg.ax with 0xF400,
Also as bzt mentioned, you need to give it a signature to tell the BIOS your buffer is big enough to hold the version 2.0 data.
I think your biggest issue is trying to execute an INT 10h instruction while in protected mode. All of this should be done before you move to protected mode.
Ben
- http://www.fysnet.net/osdesign_book_series.htm
Re: BIOS calls for VESA functionalities
Sorry it was a typo. I put the value 0x004F in ax and not 0x00F4.BenLunt wrote:Does this mean you are executing an INT 10h instruction while in protected mode?iman wrote:It is now in protected mode and I want to call VESA functionalities.
As MichaelPetch asked, is this a typo? It should be 0x4F00.iman wrote:Then I supplied reg.ax with 0xF400,
Also as bzt mentioned, you need to give it a signature to tell the BIOS your buffer is big enough to hold the version 2.0 data.
I think your biggest issue is trying to execute an INT 10h instruction while in protected mode. All of this should be done before you move to protected mode.
Ben
- http://www.fysnet.net/osdesign_book_series.htm
I also put the 'VBE2' signature and reserved 512 bytes for the buffer. To trigger INT 10H in protected mode, I used the same function that Napalm distributed long time ago and as I said for some other calls, it works perfectly. For example, I can supply ax and bx with the needed value and call INT 10H to change the mode for me and it does the job. But for this task that I play with reg.es, although it gives me the success return value, it does not fill up the VBE_INFO buffer.
Re: BIOS calls for VESA functionalities
Dear Michael.MichaelPetch wrote:It would probably help to see your actual code.Do you have your OS on Gitlab or Github (or similar service)? Sounds like the memory address of your vbe_info (Segment and Offset) structure that you told Int 10h/04f00h to fill in. Was this a typo?Did you mean 0x4F00?Then I supplied reg.ax with 0xF400,
It was a typo during my message writing.
I put the value 0x004F in reg.ax and not 0x00F4.
I will put it public on my Github and send the link here. But, I used exactly the same function that Napalm distributed and my little OS is supposed not to do anything but draw in SVGA. Therefore, I have a kernel file and a loader file which reserve some stack, provides magic numbers for GRUB, and call the kernel. In the kernel file on the other hand, I would like to call INT 10H with the use of int32 function.
Re: BIOS calls for VESA functionalities
BenLunt wrote:Does this mean you are executing an INT 10h instruction while in protected mode?iman wrote:It is now in protected mode and I want to call VESA functionalities.
As MichaelPetch asked, is this a typo? It should be 0x4F00.iman wrote:Then I supplied reg.ax with 0xF400,
Also as bzt mentioned, you need to give it a signature to tell the BIOS your buffer is big enough to hold the version 2.0 data.
I think your biggest issue is trying to execute an INT 10h instruction while in protected mode. All of this should be done before you move to protected mode.
Ben
- http://www.fysnet.net/osdesign_book_series.htm
Yes. This is where I tried to switch back to the real-mode and trigger INT 10H.BenLunt wrote:Does this mean you are executing an INT 10h instruction while in protected mode?
-
- Member
- Posts: 798
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: BIOS calls for VESA functionalities
It is unclear how your OS in github even compiles or works as expected since you define function int32 to take 2 parameters and you only call it with one. Are you sure your github actually is a workable example.Would also help if your project contained something that had the commands used to compile/linker your files, your linker script etc.
Re: BIOS calls for VESA functionalities
Re-updated.MichaelPetch wrote:It is unclear how your OS in github even compiles or works as expected since you define function int32 to take 2 parameters and you only call it with one. Are you sure your github actually is a workable example.Would also help if your project contained something that had the commands used to compile/linker your files, your linker script etc.
You can find the working code of mine.
Besides all the commands have been given in their orders as well.
Cheers.
-
- Member
- Posts: 798
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: BIOS calls for VESA functionalities
Can you look back at my question and answer the one about function int32 defined as taking 2 parameters but you only pass 1 when you call it from kernel.c?
Re: BIOS calls for VESA functionalities
MichaelPetch wrote:Can you look back at my question and answer the one about function int32 defined as taking 2 parameters but you only pass 1 when you call it from kernel.c?
It gets two arguments. It was a mistake.
In my Github page, I updated everything. Now the status of the code is exactly what I use on both linux and windows machines that I have.
Re: BIOS calls for VESA functionalities
Hi,
I took a look at your code. There are so many things you should fix first before you try VESA.
1. first of all, you should NEVER use a hosted C compiler for a kernel. Use a cross-compiler, or at least specify "-ffreestanding -nostdlib -nostdinc". ANSI C defines two modes, freestanding and hosted for a reason.
2. why do you copy and relocate a lot on every single int32 call? Why don't you place the regs struct somewhere in memory where both the prot mode and real mode codes can access it directly?
3. your stack is very likely corrupted. Maybe I'm mistaken, but where do you push the segment registers on the stack which you pop at line 71?
4. I'm not sure that pusha struct - regs struct are the same, mostly because of the wild stack handling. It would be much faster to use a couple of mov's instead btw, but if you insist on popa / pusha, then you should set the stack at 7C00 and map that area with the regs struct from your linker script. That way you could directly update the real mode stack (which loads the registers from there with a popa) from your C code simply using the regs struct.
5. maybe it would be useful to take a look how Minix does int86(). It also uses a register structure, and it does not relocate anything (although it relies on a real mode monitor, which is implemented in its boot loader).
Cheers,
bzt
I took a look at your code. There are so many things you should fix first before you try VESA.
1. first of all, you should NEVER use a hosted C compiler for a kernel. Use a cross-compiler, or at least specify "-ffreestanding -nostdlib -nostdinc". ANSI C defines two modes, freestanding and hosted for a reason.
2. why do you copy and relocate a lot on every single int32 call? Why don't you place the regs struct somewhere in memory where both the prot mode and real mode codes can access it directly?
3. your stack is very likely corrupted. Maybe I'm mistaken, but where do you push the segment registers on the stack which you pop at line 71?
4. I'm not sure that pusha struct - regs struct are the same, mostly because of the wild stack handling. It would be much faster to use a couple of mov's instead btw, but if you insist on popa / pusha, then you should set the stack at 7C00 and map that area with the regs struct from your linker script. That way you could directly update the real mode stack (which loads the registers from there with a popa) from your C code simply using the regs struct.
5. maybe it would be useful to take a look how Minix does int86(). It also uses a register structure, and it does not relocate anything (although it relies on a real mode monitor, which is implemented in its boot loader).
Cheers,
bzt
-
- Member
- Posts: 798
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: BIOS calls for VESA functionalities
Your VBE structure will have to be below 0x100000 to properly work since BIOS interrupts will have difficulty writing above the 1MiB mark. As it is your VBE structure is on the stack where ESP > 0x100000. Pick a memory location in lower memory (like 0x600) that is above the BIOS Data area and the interrupt vector table. This address can be used for both protected mode and realmode. Your OFF and SEG macros assume realmode FAR pointers. Adjust them so they compute to a normalized segment:offset for physical address < 0x100000. The macros could look like:Your VBE function could look like: Your main could look like
Function int32 assumes that the interrupts were already on prior to being called and turns them on when finished. If you don't have a protected mode IDT set up (or you haven't disabled all the IRQs on the PICs) the code will fault in protected mode when int32 is finished. I've amended the code to only re-enable interrupts if they were already enabled when first called:
Code: Select all
#define SEG(FP) ((uint16_t)(((unsigned long)FP)>>4))
#define OFF(FP) ((uint16_t)(((unsigned long)FP)&0xF))
Code: Select all
int VBE( VBE_INFO* vbe )
{
regs16_t regs;
regs.ax = 0x4F00;
regs.es = SEG(vbe);
regs.di = OFF(vbe);
int32( 0x10, ®s );
if ( regs.ax == 0x004F )
return 1;
return 0;
}
Code: Select all
int main( void ) {
VBE_INFO *vbe = (VBE_INFO *)0x600;
int res;
vbe->signature[0] = 'V';
vbe->signature[1] = 'B';
vbe->signature[2] = 'E';
vbe->signature[3] = '2';
res = VBE( vbe );
if (res == 1)
{
if(vbe->version == 0x0000) // this is what I get always
printk('N');
else if(vbe->version != 0x0000)
printk('Y');
}
else
printk('0');
}
Code: Select all
[bits 32]
global int32, _int32
struc regs16_t
.di resw 1
.si resw 1
.bp resw 1
.sp resw 1
.bx resw 1
.dx resw 1
.cx resw 1
.ax resw 1
.gs resw 1
.fs resw 1
.es resw 1
.ds resw 1
.ef resw 1
endstruc
%define INT32_BASE 0x7C00 ; base BIOS address
%define REBASE(x) (((x) - reloc) + INT32_BASE)
%define GDTENTRY(x) ((x) << 3)
%define CODE32 GDTENTRY(1) ; 0x08
%define DATA32 GDTENTRY(2) ; 0x10
%define CODE16 GDTENTRY(3) ; 0x18
%define DATA16 GDTENTRY(4) ; 0x20
%define STACK16 (INT32_BASE - regs16_t_size)
section .text
int32: use32
_int32:
pushf
pop dword [eflags] ; Save the current CPU flags (including IF state)
cli ; disable interrupts
pusha ; save register state to 32bit stack
mov esi, reloc ; set source to code below
mov edi, INT32_BASE ; set destination to new base address
mov ecx, (int32_end - reloc) ; set copy size to our codes size: LOOK AT THE COMMENT TAGGED WITH %*%
cld ; clear direction flag (so we copy forward)
rep movsb ; do the actual copy (relocate code to low 16bit space)
jmp INT32_BASE ; jump to new code location
reloc: use32 ; %*% From here on gets copy startting from INT32_BASE
mov [REBASE(stack32_ptr)], esp ; save 32bit stack pointer
sidt [REBASE(idt32_ptr)] ; save 32bit idt pointer
sgdt [REBASE(gdt32_ptr)] ; save 32bit gdt pointer
lgdt [REBASE(gdt16_ptr)] ; load 16bit gdt pointer
lea esi, [esp+0x24] ; set position of intnum on 32bit stack
lodsd ; read intnum into eax
mov [REBASE(ib)], al ; set intrrupt immediate byte from our arguments
mov esi, [esi] ; read regs pointer in esi as source
mov edi, STACK16 ; set destination to 16bit stack
mov ecx, regs16_t_size ; set copy size to our struct size
mov esp, edi ; save destination to as 16bit stack offset
rep movsb ; do the actual copy (32bit stack to 16bit stack)
jmp word CODE16:REBASE(p_mode16) ; switch to 16bit selector (16bit protected mode)
p_mode16: use16
mov ax, DATA16 ; get our 16bit data selector
mov ds, ax ; set ds to 16bit selector
mov es, ax ; set es to 16bit selector
mov fs, ax ; set fs to 16bit selector
mov gs, ax ; set gs to 16bit selector
mov ss, ax ; set ss to 16bit selector
mov eax, cr0 ; get cr0 so we can modify it
and al, ~0x01 ; mask off PE bit to turn off protected mode
mov cr0, eax ; set cr0 to result
jmp word 0x0000:REBASE(r_mode16) ; finally set cs:ip to enter real-mode
r_mode16: use16
xor ax, ax ; set ax to zero
mov ds, ax ; set ds so we can access idt16
mov ss, ax ; set ss so they the stack is valid
lidt [REBASE(idt16_ptr)] ; load 16bit idt
popa ; load general purpose registers from 16bit stack
pop gs ; load gs from 16bit stack
pop fs ; load fs from 16bit stack
pop es ; load es from 16bit stack
pop ds ; load ds from 16bit stack
sti ; enable interrupts
db 0xCD ; opcode of INT instruction with immediate byte
ib: db 0x00
cli ; disable interrupts
xor sp, sp ; zero sp so we can reuse it
mov ss, sp ; set ss so the stack is valid
mov sp, INT32_BASE ; set correct stack position so we can copy back
pushf ; save eflags to 16bit stack
push ds ; save ds to 16bit stack
push es ; save es to 16bit stack
push fs ; save fs to 16bit stack
push gs ; save gs to 16bit stack
pusha ; save general purpose registers to 16bit stack
mov eax, cr0 ; get cr0 so we can modify it
inc eax ; set PE bit to turn on protected mode
mov cr0, eax ; set cr0 to result
jmp dword CODE32:REBASE(p_mode32) ; switch to 32bit selector (32bit protected mode)
p_mode32: use32
mov ax, DATA32 ; get our 32bit data selector
mov ds, ax ; reset ds selector
mov es, ax ; reset es selector
mov fs, ax ; reset fs selector
mov gs, ax ; reset gs selector
mov ss, ax ; reset ss selector
lgdt [REBASE(gdt32_ptr)] ; restore 32bit gdt pointer
lidt [REBASE(idt32_ptr)] ; restore 32bit idt pointer
mov esp, [REBASE(stack32_ptr)] ; restore 32bit stack pointer
mov esi, STACK16 ; set copy source to 16bit stack
lea edi, [esp+0x28] ; set position of regs pointer on 32bit stack
mov edi, [edi] ; use regs pointer in edi as copy destination
mov ecx, regs16_t_size ; set copy size to our struct size
cld ; clear direction flag (so we copy forward)
rep movsb ; do the actual copy (16bit stack to 32bit stack)
popa ; restore registers
test dword [eflags], 1<<9 ; If original interrupt state was on, issue STI
jz int_off ; otherwise keep interrupts off
sti
int_off:
ret ; return to caller
stack32_ptr: ; address in 32bit stack after we
dd 0x00000000 ; save all general purpose registers
idt32_ptr: ; IDT table pointer for 32bit access
dw 0x0000 ; table limit (size)
dd 0x00000000 ; table base address
gdt32_ptr: ; GDT table pointer for 32bit access
dw 0x0000 ; table limit (size)
dd 0x00000000 ; table base address
idt16_ptr: ; IDT table pointer for 16bit access
dw 0x03FF ; table limit (size)
dd 0x00000000 ; table base address
gdt16_base: ; GDT descriptor table
.null: ; 0x00 - null segment descriptor
dd 0x00000000 ; must be left zero'd
dd 0x00000000 ; must be left zero'd
.code32: ; 0x01 - 32bit code segment descriptor 0xFFFFFFFF
dw 0xFFFF ; limit 0:15
dw 0x0000 ; base 0:15
db 0x00 ; base 16:23
db 0x9A ; present, iopl/0, code, execute/read
db 0xCF ; 4Kbyte granularity, 32bit selector; limit 16:19
db 0x00 ; base 24:31
.data32: ; 0x02 - 32bit data segment descriptor 0xFFFFFFFF
dw 0xFFFF ; limit 0:15
dw 0x0000 ; base 0:15
db 0x00 ; base 16:23
db 0x92 ; present, iopl/0, data, read/write
db 0xCF ; 4Kbyte granularity, 32bit selector; limit 16:19
db 0x00 ; base 24:31
.code16: ; 0x03 - 16bit code segment descriptor 0x000FFFFF
dw 0xFFFF ; limit 0:15
dw 0x0000 ; base 0:15
db 0x00 ; base 16:23
db 0x9A ; present, iopl/0, code, execute/read
db 0x0F ; 1Byte granularity, 16bit selector; limit 16:19
db 0x00 ; base 24:31
.data16: ; 0x04 - 16bit data segment descriptor 0x000FFFFF
dw 0xFFFF ; limit 0:15
dw 0x0000 ; base 0:15
db 0x00 ; base 16:23
db 0x92 ; present, iopl/0, data, read/write
db 0x0F ; 1Byte granularity, 16bit selector; limit 16:19
db 0x00 ; base 24:31
gdt16_ptr: ; GDT table pointer for 16bit access
dw gdt16_ptr - gdt16_base - 1 ; table limit (size)
dd gdt16_base ; table base address
eflags: dd 0 ; Storage for EFLAGS (including IF)
int32_end: ; end marker (so we can copy the code)
Last edited by MichaelPetch on Fri Feb 08, 2019 9:13 am, edited 1 time in total.
-
- Member
- Posts: 798
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: BIOS calls for VESA functionalities
.They aren't pushed, but they are copied in from the memory address passed as a regs16_t * to the 16-bit real mode stack with these lines:bzt wrote:3. your stack is very likely corrupted. Maybe I'm mistaken, but where do you push the segment registers on the stack which you pop at line 71?
Code: Select all
mov esi, [esi] ; read regs pointer in esi as source
mov edi, STACK16 ; set destination to 16bit stack
mov ecx, regs16_t_size ; set copy size to our struct size
mov esp, edi ; save destination to as 16bit stack offset
rep movsb ; do the actual copy (32bit stack to 16bit stack)