OSDev.org https://forum.osdev.org/ |
|
BIOS calls for VESA functionalities https://forum.osdev.org/viewtopic.php?f=1&t=33486 |
Page 2 of 2 |
Author: | iman [ Fri Feb 08, 2019 4:40 am ] |
Post subject: | Re: BIOS calls for VESA functionalities |
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 Cheers. Iman. |
Author: | iman [ Fri Feb 08, 2019 4:59 am ] |
Post subject: | Re: BIOS calls for VESA functionalities |
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)) Your VBE function could look like: #define OFF(FP) ((uint16_t)(((unsigned long)FP)&0xF)) Code: int VBE( VBE_INFO* vbe ) Your main could look like { 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: 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. |
Author: | MichaelPetch [ Fri Feb 08, 2019 8:45 am ] |
Post subject: | Re: BIOS calls for VESA functionalities |
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. |
Author: | bzt [ Fri Feb 08, 2019 11:24 am ] |
Post subject: | Re: BIOS calls for VESA functionalities |
iman wrote: Thanks a lot for your comments. I'll try to answer as much as I can. 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.1. I simply sticked to the hosted C compiler, since I had never seen any problem. iman wrote: 2. This is exactly something that I will do soon. Cool Move everything you can into an intialization function, and leave only the bare minimum to the often called int32 function.If I would use int32 many times, then copying and relocating the code must be minimized. 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 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 |
Page 2 of 2 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |