Brendan wrote:
Hi,
rbmj wrote:
I'll mention this thread:
http://f.osdev.org/viewtopic.php?f=1&t=9621According to the third post, the VBE Far Pointer can be either a Real or Protected Mode pointer.
Think of it like this:
Code:
typedef struct {
uint16_t offset;
uint16_t segment;
} vbeFarPtr;
The "vbeFarPtr" might contain a real mode segment and offset, or it might contain a 16-bit protected mode selector and offset, and the BIOS/VBE doesn't need to care because it can just use "LDS" or "call far" or whatever (and treats it as a 32-bit far pointer, consisting of a 16-bit "something" and a 16-bit offset) and the BIOS/VBE doesn't need to care if it's running in real mode or 16-bit protected mode. The only thing that matters is that the caller sets it up correctly before calling the BIOS/VBE.
Unfortunately, the information above, and the information quoted in the third post of the thread you linked to, is talking about the "VBE 3.0" protected mode interface (which uses 16-bit protected mode); and has nothing at all to do with the old "VBE 2.0" protected mode interface (which uses 32-bit protected mode).
rbmj wrote:
If grub gets it from int 10h, and then switches to protected mode, then it's a real mode pointer, in which case i believe i should linearize.
It's a real mode pointer, taken directly from the BIOS/VBE function. If GRUB did anything it'd convert it to a 32-bit physical address instead.
rbmj wrote:
Since they use segment, it leads me to believe it's a real mode far pointer, but then why are all of these fields 32 bits (instead of a single 32 bit far pointer ie each is 16 bits)?
Multi-boot spec wrote:
Code:
82 | vbe_interface_seg |
84 | vbe_interface_off |
86 | vbe_interface_len |
+-------------------+
They are all 16-bit values, not 32-bit fields.
So, to clarify the entire thing...
For the "VBE 2.0" 32-bit protected mode interface:- Use the vbe_interface_seg and vbe_interface_off fields from multi-boot to calculate the (32-bit) physical address of the table. This will probably be in the video card's ROM (e.g. "0x000C????").
- Use the word at offset 6 in the table to determine the address of the "ports and memory" list
- Use the address of the "ports and memory" list to check if a memory area is required
- If a memory area is required, setup a segment in your GDT for this memory area (where the segment's base address is equal to whatever the "ports and memory" list says)
- To call a function via. the protected mode interface table:
- Load the "memory area" segment into DS or ES (if required by the function you're calling)
- Find the address of the function by adding the offset for that function to the physical address of the table (e.g. if during step 1 you calculated that the table is at physical address 0x000C1234 and you want to call the "Display Start" function, then you'd get the word at offset 2 in the table and add that value to 0x000C1234 to find the address to call
- Call the address you calculated in the previous step, using a "call near"
- After all this works, find out that most cards don't support it and it's useless for everything (except bank switching) even when it is supported
For the "VBE 3.0" 16-bit protected mode interface:- Search the first 32 KiB of the ROM area (at 0x000C0000) for the "PMID" signature, and if you find the signature calculate the checksum to make sure it actually is the table and not just an accident. The spec says to do this after you allocated RAM, etc, but that's stupid (you wouldn't allocate RAM unless you know the protected mode interface exists).
- Copy the entire ROM image to allocated RAM. The spec says "32 KiB normally" but I couldn't see a sane way to determine the ROM's actual size, so maybe just copy 32 KiB and cross your fingers
- Allocate another area of RAM (at least 0x600 bytes) to use for a pretend BDA and fill it with zeros
- Create GDT entries for the areas at 0x000A0000, 0x000B0000, 0x000B8000, the RAM you allocated for the copy of the ROM image and the RAM you allocated pretend BDA area
- Put all of these selectors into the "PMInfoBlock" structure that you found (in the copy in RAM, not the original copy in ROM)
- Set the "In protected mode flag" in the "PMInfoBlock" structure that you found
- Create a GDT entry for a 16-bit code segment that points to your 32 KiB copy of the ROM, and another GDT entry for a 16-bit stack (that's a total of 7 GDT entries now)
- To call the thing:
- Switch to the 16-bit stack (and set ESP to zero)
- Do a "far call" to the entry point (the offset of the entry point is in the "PMInfoBlock" structure that you found. Note: You must call the "PMInitialize" entry point before using any of the normal VBE functions; and then use the "EntryPoint" entry point afterwards to call the normal VBE functinos.
- After all this works, find out that most cards don't support it, and that for cards that actually do support it the video card's ROM is full of bugs
rbmj wrote:
I don't exactly understand why the sample kernel just randomly decides to stop parsing the info struct after the memory map- makes me feel like grub doesn't support any of the other fields, which makes any attempt to write support code for this data is a waste of time...
In which way to you not understand that "sample kernel" is not the same as "real kernel"?
Cheers,
Brendan
Hi Brendan,
I have problem with call VBE function, the problem is how to switch to 16 bit protect mode stack before call function.
My code will be crashed when call.
i'm using OpenWatcom C++ 2.0, OS: MS-DOS (32bits)
Code:
#define VBE_DATA_SIZE 0x600 // VBE data size
#define VBE_STACK_SIZE 0x2000 // VBE stack size
#define VBE_CODE_SIZE 0x8000 // VBE code size
typedef struct
{
unsigned int offset;
unsigned short segment;
} VBE_FAR_CALL;
typedef struct
{
unsigned int esp;
unsigned short ss;
} VBE_USER_STACK;
VBE_USER_STACK stack;
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 into 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;
stack.esp = VBE_STACK_SIZE;
stack.ss = biosStackSel;
_asm {
lea eax, stack
lss esp, [eax] // load stack segment
jmp fword ptr [eax] // switch to stack adress
lea esi, fcall // make ptr48 function address
call fword ptr [esi] // make far call to BIOS function
}
Please help me!
Many thanks in advanced!
PheroSiden