OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 16, 2024 4:43 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: Problem with VESA 3.0 in Protect Mode
PostPosted: Wed Jun 20, 2018 10:32 pm 
Offline

Joined: Wed Jun 20, 2018 2:28 am
Posts: 3
Hello World!

I'm try to call VESA 3.0 function from BIOS in protected mode but crash with general protection fault. I followed VESA 3.0 specf but no details information how to do this.
I use OpenWatcom C++ 2.0 and MS-DOS 32 bits environment.
and here is my code...

Code:
// VESA 3.0 protected mode info block
typedef struct
{
   unsigned char   Signature[4];         // PM Info Block signature ('PMID')
   unsigned short   EntryPoint;         // offset of PM entry point within BIOS
   unsigned short   PMInitialize;         // offset of PM initialization entry point
   unsigned short   BIOSDataSel;         // selector to BIOS data area emulation block
   unsigned short   A0000Sel;            // selector to 0xa0000
   unsigned short   B0000Sel;            // selector to 0xb0000
   unsigned short   B8000Sel;            // selector to 0xb8000
   unsigned short   CodeSegSel;         // selector to access code segment as data
   unsigned char   InProtectMode;         // true if in protected mode
   unsigned char   Checksum;         // sum of all bytes in this struct must match 0
} VBE_PM_INFO_BLOCK;

// VESA 3.0 far call memory struct (48 bits address)
typedef struct
{
   unsigned int   offset;            // 32 bits offset
   unsigned short   segment;            // 16 bits segment
} VBE_FAR_CALL;

// switch to 16 bit protect mode stack
void StackSwitch(void *mesp, void *mss)
{
   _asm {
      mov    eax, dr3
      mov    edx, mesp
      pushf
      pop    ecx
      add    eax, stackOffset
      cli
      push   ss
      cmp    [esp], 0x10
      je     kernelStack
      pop    eax
      jmp    switchStack
   kernelStack:
      pop    [eax + 4]
      mov    esp, [eax]
   switchStack:
      lss      esp, mss
      push   ecx
      popf
      jmp      fword ptr [edx]
   }
}

int main(int argc, char const *argv[])
{
   VBE_FAR_CALL      fcall;
   VBE_DRIVER_INFO      drvInfo;
   VBE_PM_INFO_BLOCK   *pmInfo;

   unsigned int val = 0;
   unsigned int i = 0;
   unsigned char biosCheckSum = 0;

   unsigned char *biosCode;
   unsigned char *biosData;
   unsigned char *biosStack;
   unsigned char *biosPtr;

   unsigned short biosDataSel;
   unsigned short biosCodeSel;
   unsigned short a0000Sel;
   unsigned short b0000Sel;
   unsigned short b8000Sel;

   unsigned short biosInitSel;
   unsigned short biosStackSel;
   
   unsigned short vbeInfoSel;

   // copy BIOS code from physical address 0xC0000 to RAM
   biosCode = (unsigned char*)malloc(VBE_CODE_SIZE);
   if (!biosCode) return 1;
   memcpy(biosCode, (unsigned char*)0xC0000, VBE_CODE_SIZE);

   // find VESA 3.0 protected mode info block signature
   biosPtr = biosCode;
   while ((biosPtr <= biosCode + VBE_CODE_SIZE - sizeof(VBE_PM_INFO_BLOCK)) && memcmp(((VBE_PM_INFO_BLOCK*)biosPtr)->Signature, "PMID", 4)) biosPtr++;

   // check for correct signature
   pmInfo = (VBE_PM_INFO_BLOCK*)biosPtr;
   if (memcmp(pmInfo->Signature, "PMID", 4))
   {
      printf("VESA PMID not found!\n");
      return 1;
   }

   // calculate BIOS checksum
   for (i = 0; i != sizeof(VBE_PM_INFO_BLOCK); i++) biosCheckSum += *biosPtr++;
   if (biosCheckSum)
   {
      printf("VESA BIOS checksum error!\n");
      return 1;
   }

   // setup structure (provide selectors, map video mem, ...)
   biosData = (unsigned char *)malloc(VBE_DATA_SIZE);
   if (!biosData) return 1;
   memset(biosData, 0, VBE_DATA_SIZE);

   // setup BIOS data selector
   biosDataSel = AllocSelector();
   if (biosDataSel == 0 || biosDataSel == 0xFFFF) return 1;
   if (!SetSelectorRights(biosDataSel, 0x8092)) return 1;
   if (!SetSelectorBase(biosDataSel, (unsigned int)biosData)) return 1;
   if (!SetSelectorLimit(biosDataSel, VBE_DATA_SIZE - 1)) return 1;
   pmInfo->BIOSDataSel = biosDataSel;

   // map video memory
   a0000Sel = AllocSelector();
   if (a0000Sel == 0 || a0000Sel == 0xFFFF) return 1;
   if (!SetSelectorRights(a0000Sel, 0x8092)) return 1;
   if (!SetSelectorBase(a0000Sel, (unsigned int)0xA0000)) return 1;
   if (!SetSelectorLimit(a0000Sel, 0xFFFF)) return 1;
   pmInfo->A0000Sel = a0000Sel;

   b0000Sel = AllocSelector();
   if (b0000Sel == 0 || b0000Sel == 0xFFFF) return 1;
   if (!SetSelectorRights(b0000Sel, 0x8092)) return 1;
   if (!SetSelectorBase(b0000Sel, (unsigned int)0xB0000)) return 1;
   if (!SetSelectorLimit(b0000Sel, 0xFFFF)) return 1;
   pmInfo->B0000Sel = b0000Sel;

   b8000Sel = AllocSelector();
   if (b8000Sel == 0 || b8000Sel == 0xFFFF) return 1;
   if (!SetSelectorRights(b8000Sel, 0x8092)) return 1;
   if (!SetSelectorBase(b8000Sel, (unsigned int)0xB8000)) return 1;
   if (!SetSelectorLimit(b8000Sel, 0x7FFF)) return 1;
   pmInfo->B8000Sel = b8000Sel;

   // setup BIOS code selector
   biosCodeSel = AllocSelector();
   if (biosCodeSel == 0 || biosCodeSel == 0xFFFF) return 1;
   if (!SetSelectorRights(biosCodeSel, 0x8092)) return 1;
   if (!SetSelectorBase(biosCodeSel, (unsigned int)biosCode)) return 1;
   if (!SetSelectorLimit(biosCodeSel, VBE_CODE_SIZE - 1)) return 1;
   pmInfo->CodeSegSel = biosCodeSel;

   // put BIOS code to run in protect mode
   pmInfo->InProtectMode = 1;

   // alloc code segment selector for initialize function
   biosInitSel = AllocSelector();
   if (biosInitSel == 0 || biosInitSel == 0xFFFF) return 1;
   if (!SetSelectorRights(biosInitSel, 0x8092)) return 1;
   if (!SetSelectorBase(biosInitSel, (unsigned int)biosCode)) return 1;
   if (!SetSelectorLimit(biosInitSel, VBE_CODE_SIZE - 1)) return 1;

   // alloc stack selector
   biosStack = (unsigned char *)malloc(VBE_STACK_SIZE);
   if (!biosStack) return 1;
   biosStackSel = AllocSelector();
   if (biosStackSel == 0 || biosStackSel == 0xFFFF) return 1;
   if (!SetSelectorRights(biosStackSel, 0x8092)) return 1;
   if (!SetSelectorBase(biosStackSel, (unsigned int)biosStack)) return 1;
   if (!SetSelectorLimit(biosStackSel, VBE_STACK_SIZE - 1)) return 1;

   // call initialize protect mode function first
   fcall.offset = pmInfo->PMInitialize;
   fcall.segment = biosInitSel;
   _asm {
      push   biosStackSel
      push   VBE_STACK_SIZE
      call   StackSwitch
      lea    esi, fcall
      call   fword ptr [esi]
        call   StackSwitch
   }
//// CRASHED HERE: GENERAL PROTECTION FAULT!!!!!!
   // call initialize VBE controller
   vbeInfoSel = AllocSelector();
   if (vbeInfoSel == 0 || vbeInfoSel == 0xFFFF) return 1;
   if (!SetSelectorRights(vbeInfoSel, 0x8092)) return 1;
   if (!SetSelectorBase(vbeInfoSel, (unsigned int)&drvInfo)) return 1;
   if (!SetSelectorLimit(vbeInfoSel, sizeof(VBE_DRIVER_INFO) - 1)) return 1;

   fcall.offset = pmInfo->EntryPoint;
   fcall.segment = pmInfo->CodeSegSel;
   _asm {
      push   biosStackSel
      push   VBE_STACK_SIZE
      call   StackSwitch
      mov    ax, vbeInfoSel
      mov    es, ax
      mov    eax, 0x4F00
      xor    edi, edi
      lea    esi, fcall
      call   fword ptr [esi]
      call   StackSwitch
                mov    val, eax
   }

   if (val == 0x004F && !memcmp(drvInfo.VBESignature, "VESA", 4) && drvInfo.VBEVersion >= 0x0200) printf("OK!\n");
   else printf("VESA 3.0 INIT FAILED!\n");

   return 0;
}


Anyone help me,
Thanks in advanced!

PheroSiden


Top
 Profile  
 
 Post subject: Re: Problem with VESA 3.0 in Protect Mode
PostPosted: Thu Jun 21, 2018 10:00 pm 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

pherosiden wrote:
I use OpenWatcom C++ 2.0 and MS-DOS 32 bits environment.


What is "MS-DOS 32 bits environment"?

pherosiden wrote:
and here is my code...


Never touch the stack pointer in C code. It belongs to the compiler.

Code:
// switch to 16 bit protect mode stack
void StackSwitch(void *mesp, void *mss)
{

// Compiler inserts "random" function prologue code here that does anything the compiler feels like with the stack

   _asm {
      mov    eax, dr3
      mov    edx, mesp
      pushf
      pop    ecx
      add    eax, stackOffset
      cli
      push   ss
      cmp    [esp], 0x10
      je     kernelStack
      pop    eax
      jmp    switchStack
   kernelStack:
      pop    [eax + 4]
      mov    esp, [eax]
   switchStack:
      lss      esp, mss
      push   ecx
      popf
      jmp      fword ptr [edx]
   }

// Compiler inserts "random" function epilogue code here to match whatever the compiler felt like doing in the prologue

}


Mostly, this sort of thing should be some kind of "load segments, switch stack, call VBE, switch stack back, restore segments" routine in pure (external and not inline) assembly.


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
 Post subject: Re: Problem with VESA 3.0 in Protect Mode
PostPosted: Wed Jun 27, 2018 1:52 am 
Offline

Joined: Wed Jun 20, 2018 2:28 am
Posts: 3
Hi Brendan,

i'm just follow your guide, put some code in external ASM file and then link to main file, but no results, program crashed with general protection fault.
and here is my code (vesa_stack.asm)

Code:
.model compact, c
.386
.data
   VBE_STACK_SIZE   equ   2000h   ; VBE caller stack size
   KERNEL_DATA_SEG   equ   10h      ; kernel data segment

   extern call_addr   : dword   ; VBE far call address
   extern stack_sel   : dword   ; VBE caller stack selector
   stack_offset   dd   0      ; current stack offset

.code
   public VesaCall

; 16 bit protected mode stack switching
; struct VBE_CALL_STACK {
;   unsigned int *esp;
;   unsigned int *ss
;}
StackSwitch proc
   mov      eax, dr3   ; get current thread
   mov      edx, [esp]   ; get top of stack pointer
   pushf
   pop      ecx
   add      eax, stack_offset
   cli
   push   ss
   cmp      dword ptr [esp], KERNEL_DATA_SEG
   je      kernel_stack
   pop      eax
   jmp      switch_stack
kernel_stack:
   pop      [eax + 4]
   mov      [eax], esp
switch_stack:
   lss      esp, [esp + 4]
   push   ecx
   popf
   jmp      fword ptr [edx]
StackSwitch endp

; call VESA BIOS function
; VesaCall(REGS16 *regs)
VesaCall proc
      pushf
      cli
      push   es
      pusha
      mov      esi, esp
      push   stack_sel
      push   VBE_STACK_SIZE
      call   StackSwitch
      push   KERNEL_DATA_SEG
      push   esi
      push   edi
      push   ds
      mov      dx, [edi + 12]
      mov      es, dx
      mov      ax, [edi]
      mov      bx, [edi + 2]
      mov      cx, [edi + 4]
      mov      dx, [edi + 6]
      mov     si, [edi + 8]
      mov     di, [edi + 10]
      mov     esi, call_addr
      call    fword ptr [esi]
      pop     ds
      pop      ebp
      mov      ds:[ebp], ax
      mov      eax, ebp
      mov      [eax + 2], bx
      mov      [eax + 4], cx
      mov      [eax + 6], dx
      mov      [eax + 8], si
      mov      [eax + 10], di
      mov     bx, es
      mov     [eax + 12], bx
      call    StackSwitch
      popa
      pop      es
      popf
VesaCall endp

end


and here is C code (vesa_bios.c)

Quote:
#pragma pack(push, 1)
typedef struct {
unsigned short ax;
unsigned short bx;
unsigned short cx;
unsigned short dx;
unsigned short si;
unsigned short di;
unsigned short es;
} REGS16;

typedef struct {
unsigned int *esp;
unsigned int *ss;
} VBE_CALL_STACK;
#pragma pack(pop)

void far *call_addr = NULL;
unsigned int stack_sel = 0;

extern void VesaCall(REGS16 *regs);

int main(int argc, char const *argv[])
{
REGS16 regs;
...
// call initialize protect mode function first
fcall.offset = pmInfo->PMInitialize;
fcall.segment = biosInitSel;
call_addr = (void far*)&fcall;
stack_sel = biosStackSel;
memset(&regs, 0, sizeof(REGS16));
VesaCall(&regs);
return 1;
}


i spend more time to this, can you correct my code? or any suggestion.

Cheers,
PheroSiden


Top
 Profile  
 
 Post subject: Re: Problem with VESA 3.0 in Protect Mode
PostPosted: Wed Jun 27, 2018 8:34 am 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1925
Location: Athens, GA, USA
pherosiden wrote:
i'm just follow your guide,


When you say 'guide', are you referring to his comment now, or one of the various Tutorials that are around? AFAIK, Brendan hasn't written any, and is very adamant that they are not a Good Thing for OS-Dev - the one known as "Bran's Kernel Development Guide" was written by someone else, and has several known flaws as well as being woefully outdated.

If it is a tutorial, could you please tell us which one are you using? A link to the specific page(s) would be appreciated, so we can see exactly what you are reading.

I should add that we generally advise against a) writing your own boot loader, and b) using any of the VESA 3.0 functions (which were never widely supported, even when VESA still mattered; these days, even VESA 2.0 functions may not be present on current hardware, IIUC). We won't stop you, and will help you write that it if you meant to do so, but as a rule we recommend using an existing bootloader such as GRUB (or just the built-in UEFI loader, if it is a system built after 2010 or so) and getting the initial video setting hooks from that.

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: Problem with VESA 3.0 in Protect Mode
PostPosted: Wed Jun 27, 2018 1:32 pm 
Offline
Member
Member
User avatar

Joined: Fri Feb 17, 2017 4:01 pm
Posts: 642
Location: Ukraine, Bachmut
Schol-R-LEA wrote:
I should add that we generally advise against a) writing your own boot loader, and b) using any of the VESA 3.0 functions (which were never widely supported, even when VESA still mattered; these days, even VESA 2.0 functions may not be present on current hardware, IIUC). We won't stop you, and will help you write that it if you meant to do so, but as a rule we recommend using an existing bootloader such as GRUB (or just the built-in UEFI loader, if it is a system built after 2010 or so) and getting the initial video setting hooks from that.

I am not inclined to advise anything at all, just because I am a newbass. but I wanted to say my opinion on this. I believe it's a very intrusive recommendation and I very doubt it is a good fit for everybody.
For example, I consider an OS loader a part of my OS, that understands what OS needs and, not less important, - what it doesn't nor cares about. no grubs or anything could serve such a role. they spit on me absolutely irrelevant concepts and conventions I couldn't care less about. I need a delegate into FW that working as FW payload/client/application - taking advantages of its services, will prepare environment for my OS, not for every OS on this planet and in the neighbourhood. It needs to know all inerfacing protocols and conventions required for the preparation and those I need to define, not some dudes.
Finally, there is no "built-in" UEFI loader at all. UEFI expects you to write an OS loader for your OS and it runs it as its application and gives to it its services - exactly what I described above. "Built-in" in UEFI is the Boot Manager, it's a thing that deals with exctly interacting with user and letting them to choose/set what OS loaders to run in which order. Loaders not OSes. The fact linux screwed it up embedding into itself an efi application, turning itself into a OS/efi loader mutant doesn't change the paradigm. The same was in ARC i believe and the same is on OF. And by btw, with a good FW, writing an OS loader is not complex. Most complexities arise on the CPU architecture side and OS requirements side, not on the FW side, the latter turns into something similar to writing a banal C application heavily relying on C library.
Unifying loaders is as bad as unifying driver models across OSes - reminds me that amazing cover parody you posted here. Image

Image

_________________
ANT - NT-like OS for x64 and arm64.
efify - UEFI for a couple of boards (mips and arm). suspended due to lost of all the target park boards (russians destroyed our town).


Top
 Profile  
 
 Post subject: Re: Problem with VESA 3.0 in Protect Mode
PostPosted: Thu Jun 28, 2018 4:47 am 
Offline
Member
Member
User avatar

Joined: Sat Jan 15, 2005 12:00 am
Posts: 8561
Location: At his keyboard!
Hi,

pherosiden wrote:
i spend more time to this, can you correct my code? or any suggestion.


Some notes..

1) You can expect that all of VBE's functions (especially the ones to get EDID, set a video mode, update the palette and change the display start) will take a relatively large amount of time (potentially tens of milliseconds), and for a real OS (that has to care about dropping packets from the network cards, missing IRQs from timers, etc) this is far too long to have IRQs disabled. For this reason you shouldn't disable IRQs while running VBE functions.

2) When running VBE's code; the descriptor used for SS must have the "big" flag clear so that instructions that implicitly use the stack pointer (including "push", "pop", "call", ...) will use SP and not ESP.

3) When an interrupt (including those that can't be disabled - exceptions, NMI, machine check) interrupts CPL=0 code the stack is not normally switched. This means that when VBE's code is interrupted your interrupt handler will be started with a 16-bit stack (due to the "big flag clear" mentioned above). Note: The alternative would be to run VBE's code at CPL=3 and emulate any protected instructions (HLT, CLI, STI, ...), but if you were doing that it'd make more sense to use virtual8086 mode and not bother with VBE's protected mode interface at all.

4) To make some of the above easier to deal with; I'd recommend having a 32-bit stack in the first 64 KiB of the address space; so that the highest bits of ESP are zero and so that ESP is equal to SP. This allows you to switch from a 32-bit stack ("big" flag set) to a 16-bit stack ("big" flag clear) without changing ESP at all (just by loading SS alone), and without changing where the stack is in the address space.

5) All of the above combined would allow you to delete the entire "StackSwitch" procedure, and just have a "mov ss" (to switch to a 16-bit stack) immediately before calling the VBE function and another "mov ss" (to switch back to a 32-bit stack) immediately after calling the VBE function.

6) You should be able to test most of the code without actually calling any VBE function (just remove the "call fword [call_addr]" and make sure your code switches segment registers, etc correctly). You should also be able to use a breakpoint (e.g. the "xchg bx,bx" magic breakpoint in Bochs) and manually check that everything is right at the point where you would've called a VBE function; and should be able to deliberately cause an exception (e.g. "int3") to make sure your interrupt/exception handlers work correctly.

7) If your interrupt handlers work correctly; for some VBE functions (e.g. "Return VBE Controller Information") you should be able to display all of the information that might be useful to figure out what went wrong (contents of all the registers, value in EIP, exception number, exception's error code, ...). For other VBE functions ("Set VBE Mode") video output may be in an undefined state; but hopefully if some VBE functions work they all will.


Cheers,

Brendan

_________________
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 1085 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