OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 6:32 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 19 posts ]  Go to page Previous  1, 2
Author Message
 Post subject: Re: BIOS calls for VESA functionalities
PostPosted: Fri Feb 08, 2019 4:40 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 06, 2019 10:41 am
Posts: 84
bzt wrote:
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


Thanks a lot for your comments. I'll try to answer as much as I can.
1. I simply sticked to the hosted C compiler, since I had never seen any problem.
2. This is exactly something that I will do soon.
If I would use int32 many times, then copying and relocating the code must be minimized.
3. In lines 49 and 50, I somehow pushed the segment registers on 16-bit stack which later on I pop them at line 71.
4. Since I got this code from somewhere else, I had also the same feeling that pusha and struct regs16_t might not match exactly, but I double checked them and confirm that both behaves similarly.
5. Thanks for the link :D

Cheers.
Iman.

_________________
Iman Abdollahzadeh
Github
Codeberg


Top
 Profile  
 
 Post subject: Re: BIOS calls for VESA functionalities
PostPosted: Fri Feb 08, 2019 4:59 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 06, 2019 10:41 am
Posts: 84
MichaelPetch wrote:
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:
Code:
#define SEG(FP) ((uint16_t)(((unsigned long)FP)>>4))
#define OFF(FP) ((uint16_t)(((unsigned long)FP)&0xF))
Your VBE function could look like:
Code:
int VBE( VBE_INFO* vbe )
{
        regs16_t regs;
        regs.ax = 0x4F00;
        regs.es = SEG(vbe);
        regs.di = OFF(vbe);
        int32( 0x10, &regs );
        if ( regs.ax == 0x004F )
                return 1;
        return 0;
}
Your main could look like
Code:
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');
}

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:
[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

        and 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)

Thanks MichaelPetch for your comprehensive comment and code. Regarding the fact that int32, in case of lack of p-mode IDT table would make a fault, I confirm that. Since I wan exactly aware of what might have been the source of problem, for now, I just ignored it. With your updated code of int32, now, I got the reason for such a fault and learnd how to fix it.

With the use of direct memory assigning for VBE_INFO struct, say at 0x600, the in32 call now is successful.
This is another new fact that I learned from this point :). Now it arises another inquiry:

based on the structure below, there are quite some free bytes below 1 Mb. Could it be always free?, so once in protected mode I can assign memory blocks for structs such as VESA-related structs?
0x00000 - 0x003FF - Real Mode IVT
0x00400 - 0x004FF - BIOS Data Area
0x00500 - 0x07BFF - Unused
0x07C00 - 0x07DFF - Bootloader
0x07E00 - 0x9FFFF - Unused
0xA0000 - 0xBFFFF - Video RAM
.
.
.

Cheers.
Iman.

_________________
Iman Abdollahzadeh
Github
Codeberg


Top
 Profile  
 
 Post subject: Re: BIOS calls for VESA functionalities
PostPosted: Fri Feb 08, 2019 8:45 am 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
It is more like this:

0x00000000 0x000003FF 1 KiB RAM - partially unusable (see above) Real Mode IVT (Interrupt Vector Table)
0x00000400 0x000004FF 256 bytes RAM - partially unusable (see above) BDA (BIOS data area)
0x00000500 0x00007BFF almost 30 KiB RAM (guaranteed free for use) Conventional memory
0x00007C00 (typical location) 0x00007DFF 512 bytes RAM - partially unusable (see above) Your OS BootSector
0x00007E00 0x0007FFFF 480.5 KiB RAM (guaranteed free for use) Conventional memory
0x00080000 0x0009FFFF 128 KiB RAM - partially unusable (see above) EBDA (Extended BIOS Data Area)
0x000A0000 0x000FFFFF 384 KiB various (unusable) Video memory, ROM Area

The EBDA is just below video memory at 0xA0000. More about that can be found in this OSDev wiki article. I use 0x600 out of habit. In the old days (35 years ago) some ancient PCs used the memory between 0x500 and 0x520 but that hasn't been the case in a long time. If you were using paging (in a V8086 task) you might consider 0x1000 which is the lowest on a 4KiB page boundary. The int32 code itself sets the realmode stack to grow down from 0x7c00. The main int32 code and data is in the memory area where a bootloader may have been (around 0x7c00) that code and data as it is is less than 512 bytes.


Top
 Profile  
 
 Post subject: Re: BIOS calls for VESA functionalities
PostPosted: Fri Feb 08, 2019 11:24 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
iman wrote:
Thanks a lot for your comments. I'll try to answer as much as I can.
1. I simply sticked to the hosted C compiler, since I had never seen any problem.
Haven't seen YET. Believe me (or ask any other forum member), hosted C can cause some very nasty bugs in your kernel. Add those flags for your own sake.
iman wrote:
2. This is exactly something that I will do soon.
If I would use int32 many times, then copying and relocating the code must be minimized.
Cool :-) Move everything you can into an intialization function, and leave only the bare minimum to the often called int32 function.
iman wrote:
3. In lines 49 and 50, I somehow pushed the segment registers on 16-bit stack which later on I pop them at line 71.
Yeah, now I can see that. But it's an extremely ugly hack if I may.
iman wrote:
4. Since I got this code from somewhere else, I had also the same feeling that pusha and struct regs16_t might not match exactly, but I double checked them and confirm that both behaves similarly.
That's good. I wasn't sure. Also it's great that you double check "borrowed" code.
iman wrote:
5. Thanks for the link :D
Welcome!
iman wrote:
some free bytes below 1 Mb. Could it be always free?
Use E820 to be sure. But 0x600-0x80000 supposed to be free on an IBM PC compatible (once your kernel loaded, bootloader area 7C00 can be freely overwritten).
MichaelPetch wrote:
I use 0x600 out of habit. In the old days (35 years ago) some ancient PCs used the memory between 0x500 and 0x520 but that hasn't been the case in a long time.
I also relocate my code to 0x600. I can confirm that some BIOSes still use 0x500-0x600. In the old days that area was reserved for the ROM BASIC, and therefore some BIOS manufacturers though it's safe to use for other purposes.

Cheers,
bzt


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page Previous  1, 2

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], SemrushBot [Bot] and 64 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group