OSDev.org

The Place to Start for Operating System Developers
It is currently Sun Sep 20, 2020 5:40 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Updated multiboot reference implementation?
PostPosted: Wed May 11, 2011 4:17 pm 
Offline

Joined: Tue Jan 13, 2009 4:24 pm
Posts: 11
I'm working on parsing all of the data in the multiboot information structure (well, everything that's there, at least), and am working on all of the tables it provides. GRUB's code has been invaluable in helping to figure out the ambiguities (like how exactly to handle a structure with a size field at negative offset- who came up with that?). However, it seems like this code does not contain anything beyond the mmap fields. Is there an updated reference implementation out there that my google-foo can't seem to find?

Also, I've seen some stuff about multiboot 2. The only real differences I saw in a quick glance was an updated magic number and a few minor changes to flags and stuff. Is there any advantage to using multiboot 2 over multiboot 1?


Top
 Profile  
 
 Post subject: Re: Updated multiboot reference implementation?
PostPosted: Thu May 12, 2011 2:00 pm 
Offline

Joined: Tue Jan 13, 2009 4:24 pm
Posts: 11
As far as malloc is concerned, I would conceptualize it something like a struct with a first field set to sizeof second field, and then giving &second to the user. I wouldn't call it a negative offset, just a pointer to the middle of the structure...

Does anyone know how GRUB gets the VBE pmode table? I don't know if the segment/offset pair is a real mode or a protected mode far pointer (I would assume the former, but VESA says that it gives a protected mode descriptor/offset pair in the pmode table, and the fields are each 32 bits...). If its a real mode, then I can just linearize it afaik (please correct me if i'm wrong), but if it's a protected mode, i'd have to worry about mucking things up when I install my own gdt, and possibly have to switch out ds and/or cs every time i wished to access the interface, and then switch it back (right?). Would my best bet be to check grub's source?

And why does grub's sample code only handle ~50% of the info structure?


Top
 Profile  
 
 Post subject: Re: Updated multiboot reference implementation?
PostPosted: Fri May 13, 2011 1:24 am 
Offline
Member
Member
User avatar

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

rbmj wrote:
Does anyone know how GRUB gets the VBE pmode table? I don't know if the segment/offset pair is a real mode or a protected mode far pointer (I would assume the former, but VESA says that it gives a protected mode descriptor/offset pair in the pmode table, and the fields are each 32 bits...). If its a real mode, then I can just linearize it afaik (please correct me if i'm wrong), but if it's a protected mode, i'd have to worry about mucking things up when I install my own gdt, and possibly have to switch out ds and/or cs every time i wished to access the interface, and then switch it back (right?). Would my best bet be to check grub's source?


I'd assume GRUB takes the table "as is" from VBE. For the VBE 2.0 protected mode interface, you're meant to copy the table somewhere and setup your GDT to suit, then set the "selector" fields in the table to whatever you chose to use in your GDT and set the "in protected mode flag" before calling the function/s at the corresponding offset/s in the table.

Of course the VBE 2.0 protected mode interface is very limited (only useful for bank switching, and never includes functions to switch video modes or anything else), and it isn't supported by most video cards anyway, so it's probably better to ignore it completely.


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: Updated multiboot reference implementation?
PostPosted: Fri May 13, 2011 11:07 am 
Offline

Joined: Tue Jan 13, 2009 4:24 pm
Posts: 11
I'll mention this thread: viewtopic.php?f=1&t=9621

According to the third post, the VBE Far Pointer can be either a Real or Protected Mode pointer. 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. On the other hand, if grub searches for the table, then it's a protected mode selector-offset pair, in which case I should switch ds, copy the table, switch back and then install my own GDT (correct? I'm still not great with the whole segmentation thing...). Or am I completely misunderstanding everything? The multiboot specification is not specific, so either it's ambiguous or I'm missing something which is assumed knowledge. The specification says:
Quote:
The rest fields ‘vbe_interface_seg’, ‘vbe_interface_off’, and ‘vbe_interface_len’ contain the table of a protected mode interface defined in vbe 2.0+. If this information is not available, those fields contain zero.

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

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...


Top
 Profile  
 
 Post subject: Re: Updated multiboot reference implementation?
PostPosted: Fri May 13, 2011 12:27 pm 
Offline
Member
Member
User avatar

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

rbmj wrote:
I'll mention this thread: http://f.osdev.org/viewtopic.php?f=1&t=9621

According 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:
  1. 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????").
  2. Use the word at offset 6 in the table to determine the address of the "ports and memory" list
  3. Use the address of the "ports and memory" list to check if a memory area is required
  4. 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)
  5. To call a function via. the protected mode interface table:
    1. Load the "memory area" segment into DS or ES (if required by the function you're calling)
    2. 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
    3. Call the address you calculated in the previous step, using a "call near"
  6. 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:
  1. 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).
  2. 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
  3. Allocate another area of RAM (at least 0x600 bytes) to use for a pretend BDA and fill it with zeros
  4. 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
  5. Put all of these selectors into the "PMInfoBlock" structure that you found (in the copy in RAM, not the original copy in ROM)
  6. Set the "In protected mode flag" in the "PMInfoBlock" structure that you found
  7. 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)
  8. To call the thing:
    1. Switch to the 16-bit stack (and set ESP to zero)
    2. 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.
  9. 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

_________________
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: Updated multiboot reference implementation?
PostPosted: Fri May 13, 2011 12:51 pm 
Offline

Joined: Tue Jan 13, 2009 4:24 pm
Posts: 11
My god I feel like an idiot- I got lazy as there were so many 4 byte values and didn't realize that they started to change at the end!

Thank you for putting together such a complete response, and I apologize that me (my eyes?) are so lazy :P

And I do understand that sample != real- but one would think that the implementation that they provided as a reference would be a complete (at least from their perspective) reference: not doing anything with the data, but at least formatting/printing it...

Again, thanks, and sorry for being an ignorant newbie. As Heinlein would say, I am only an egg, but given time I hope to grok in fullness :D


Top
 Profile  
 
 Post subject: Re: Updated multiboot reference implementation?
PostPosted: Wed Jun 20, 2018 2:51 am 
Offline

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

rbmj wrote:
I'll mention this thread: http://f.osdev.org/viewtopic.php?f=1&t=9621

According 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:
  1. 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????").
  2. Use the word at offset 6 in the table to determine the address of the "ports and memory" list
  3. Use the address of the "ports and memory" list to check if a memory area is required
  4. 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)
  5. To call a function via. the protected mode interface table:
    1. Load the "memory area" segment into DS or ES (if required by the function you're calling)
    2. 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
    3. Call the address you calculated in the previous step, using a "call near"
  6. 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:
  1. 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).
  2. 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
  3. Allocate another area of RAM (at least 0x600 bytes) to use for a pretend BDA and fill it with zeros
  4. 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
  5. Put all of these selectors into the "PMInfoBlock" structure that you found (in the copy in RAM, not the original copy in ROM)
  6. Set the "In protected mode flag" in the "PMInfoBlock" structure that you found
  7. 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)
  8. To call the thing:
    1. Switch to the 16-bit stack (and set ESP to zero)
    2. 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.
  9. 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


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 12 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