Page 1 of 2
Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 8:49 am
by gravaera
I was reading a topic here where several users were discussing how to change video modes for VESA compliant cards. The general consensus was that it would be best to do it in Real Mode before switching to Pmode/Long Mode.
It was mentioned that changing resolutions at runtime was almost impossibly difficult. Either you entered V86 (only applicable for Pmode), or you were screwed (if you're into 64 bit. Long mode), because switching into real mode from P/Long Mode is a really painful procedure.
I came across an article where the structure of the Real Mode IVT was explained, and I realized that there could be a
VERY simple way to change video modes during OS full swing running. The IVT is simply a table that holds the segment, and offset to the function code in the BIOS shadow area.
I haven't tested this yet, or even made an attempt to, but...isn't it logical that you should be able to:
1. Lookup the location of the VGA interrupt from the IVT,
2. Copy the bytes at the segment:offset address indicated (just copy, say, 1K. You'd definitely get the whole set of functions.)
3. And use that in P/Long mode?
If the problem is that the code may be in 16bit, with 16 bit alignment, then it can be easily solved by simply copying two bytes at a time, and inserting 2 NULL bytes after each two code bytes. The instruction set is the same. So there would be no problem where the CPU's running of the code is concerned.
I could be missing something, but if not, then I got a great idea for my OS, and all of yours.
EDIT: Oh, and as a side note, I'll just mention that I've moved form the C++ party to the C party where OSDev is concerned.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 9:44 am
by dak91
Can you explain with code? This method work?
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 10:48 am
by tantrikwizard
holypanl wrote:
1. Lookup the location of the VGA interrupt from the IVT,
2. Copy the bytes at the segment:offset address indicated (just copy, say, 1K. You'd definitely get the whole set of functions.)
3. And use that in P/Long mode?
good luck with that
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:02 am
by cyr1x
holypanl wrote:
If the problem is that the code may be in 16bit, with 16 bit alignment, then it can be easily solved by simply copying two bytes at a time, and inserting 2 NULL bytes after each two code bytes. The instruction set is the same. So there would be no problem where the CPU's running of the code is concerned.
Do you really believe this?
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:06 am
by NickJohnson
I think it would probably be much easier if you just used VM86 extensions to run the BIOS in virtual real mode, instead of modifying it dynamically to run under pmode or long mode - afaik that is a relatively popular way of setting video modes after bootup. A lot of this is addressed
here.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:10 am
by cyr1x
NickJohnson wrote:I think it would probably be much easier if you just used VM86 extensions to run the BIOS in virtual real mode, ...
VM86 is not available in Long Mode. Even the link you provided states that.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:29 am
by gravaera
From the responses so far it seems like you all think this is something extremely difficult.
The IVT is an array of IVT_t entries. Any interrupt you call from asm (int 15h, or whatever you like) is simply looked up in the relevant index of the IVT. So int 15h just indexes the IVT at index [19], and loads the segment and offset and jumps to the designated area in memory.
Code: Select all
//When you call int 15h, for example, the cpu pretty much does something like this:
//Note that this code is carried out before jumping to Pmode/Long mode.
void begin_int(short int_no)
{
void (*int_func)(void);
int_func = (void(*)())((IVT[int_no].segment * 16) + IVT[int_no].offset);
(*int_func)();
};
The registers that you load contain the value the function would need to determine which subfunction to execute.
Honestly, copying the function at the segment:offset address of an IVT index couldn't be that hard. Even if you have to align it. I assume that the padding for the 16 bit code should be placed before the code, such that when the CPU loads an instruction word, the instruction is contained in AX, and the <E>AX portion (high word) has nulls. Programming a simple copy like that isn't really that difficult. If you can't make an algorithm for something as simple as that then you probably shouldn't be doing OSDev.
Code: Select all
unsigned get_vga_bios_absolute(void)
{
extern struct ivt_t *ivt = (ivt_t *) 0x00; //You could find this using [url=http://www.google.com]Google[/url]
//Also note that this assumes the IVT starts from the 1st byte.
//Vga interrupt is int 10h (int 16)
//I know the segment:offset is stored in low & high, but this is quick and dirty solution.
return ((ivt[0x10].segment * 16) + ivt[0x10].offset);
};
void copy_vga_bios(void)
{
unsigned *begin_addr = (unsigned *) get_vga_bios_absolute();
unsigned *placement_addr = 0x500; //Wherever you see fit.
for (unsigned i=0; i<1024; i++)
{
placement_addr[i] = 0x00000000;
placement_addr[i] = (*(begin_addr+i) & 0xFFFF);
};
//Padded VGA bios shadow copy complete.
// Assumptions:
//1. VGA BIOS is really only <= 1024 * (2B) long.
//The 'ret' instruction will be in there if I copied enough, so when ti VGA func finishes it will
//Just naturally return to the instruction it was at before the function was called.
I honestly don't see what was so hard about that. I did it in less than 6 minutes. If you really though copying memory from one location to another with NULL padding was hard, then wow. I wonder what will happen when you have to start reading implementing specifications that there is no real documentation on? If anything, that was excessive. I cold even have used a memcpy() if I wanted. Ease everything up.
@
cry1x: I'm a pretty serious minded guy, so yeah. I do believe it could work. From what I've read in a textbook (I may be making
this mistake again, but I assume my textbooks aren't TOTAL nonsense), Windows 2000/ME shadow copied portions of the BIOS to High Mem.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:38 am
by NickJohnson
holypanl wrote:Windows 2000/ME shadow copied portions of the BIOS to High Mem.
But that is almost definitely to run those pieces under either VM86 (2000/ME uses pmode) or an in-kernel emulator. I personally don't see what the problem is with your idea, but it seems like there could be a lot of incompatibilities. Why would Intel create VM86, and people use it for this purpose, if it was so easy simply to run real mode binaries in pmode/long mode? You can try it, but I bet you five bucks it won't work due to some unforseen hardware issue.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:46 am
by gravaera
NickJohnson wrote:holypanl wrote:Windows 2000/ME shadow copied portions of the BIOS to High Mem.
But that is almost definitely to run those pieces under either VM86 (2000/ME uses pmode) or an in-kernel emulator. I personally don't see what the problem is with your idea, but it seems like there could be a lot of incompatibilities. Why would Intel create VM86, and people use it for this purpose, if it was so easy simply to run real mode binaries in pmode/long mode? You can try it, but I bet you five bucks it won't work due to some unforseen hardware issue.
True. I'm pretty far away from going into VESA right now, since I'm a "Eleanore Semaphore" type, but I guess that's a good point.
If it does work, though, when I try it, that will be grand. I wish the IBM PC specification had a bit more...consistency. There are so many unknowns.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 11:52 am
by XanClic
tantrikwizard wrote:good luck with that
That's right. You cannot execute 16 bit code while the CPU is in 32 or 64 bit mode. You will definitely get an exception (except you try something like "cli", that's of course the same in 16 or 32 bit mode
). Example:
Will be assembled to:
Code: Select all
db 0x66, 0xB8, 0x2A, 0x00, 0x00, 0x00
(16 bit, like BIOS code)
or:
(32 bit, protected mode code)
You see the problem. If the CPU executes the 16 bit code (0x66, ...) in 32 bit mode, it thinks that stuff means:
If you have a good OS, an access to [0x2A] will result in a page fault (because that could be the use of a structure with a base on NULL).
Additionally, you don't know what's in the high part of eax, so the access will be on 0x????002A. I don't recommend executing a code in kernel mode that writes a byte to a random destination...
So the execution of 16 bit code in a 32 (or 64) bit environment WITHOUT the usage of VM86 will surely fail - in my opinion at least.
holypanl wrote:The instruction set is the same.
You see, it's
not.
If you do not trust me, try it yourself. Put
into a file named "test.asm" or something like that. Then run "nasm test.asm -o test.bin" and after that "ndisasm -u test.bin" ("-u" means disassembling in 32 bit mode).
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 12:05 pm
by Velko
Finding where the BIOS code is and copying it over is the easy part. It's still 16-bit code, but now located at different address.
By simply padding it with some NULL bytes here and there you can not turn it into working 32-bit code.
However, JIT-ing realmode code to pmode should be doable.
http://forum.osdev.org/viewtopic.php?t=10321
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 12:12 pm
by gravaera
Wow. Thanks
. I had no idea the actual binary changes between 32 and 16. I need to go read up on that...
But question: Why is it that, from what I've seen in your example, the 16 bit binary is 6 bytes long for one instruction, while the 32 bit is 5 bytes? And isn't 5 bytes a bit...odd? From every source I've read so far, the CPU picks up instructions in its WORD size.
Another thing that's really intriguing is that the hardcoded value, 0x42 is nowhere to be seen. I'm extremely intrigued. Would you mind providing a link to something to start me off clarifying this? I want to read up on that for sure.
EDIT: Okay scratch that. Thank you SO MUCH
Velko!! This is great stuff. I was really wondering how an OS in 64 bit long mode would be able to do a resolution change on the fly without doing major save of system state, and just to execute one function, and then return.
What I'll do, is continue to dev my OS, and when the GFX are sufficiently set up, and I feel it's time to get dynamic resolution changing, I'll get into the down and dirty of this stuff and figure out a way. But definitely: thanks.
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 1:15 pm
by XanClic
holypanl wrote:Another thing that's really intriguing is that the hardcoded value, 0x42 is nowhere to be seen.
Oh, sorry, I didn't meant 0x42 but simply 42 (= 0x2A).
holypanl wrote:Why is it that, from what I've seen in your example, the 16 bit binary is 6 bytes long for one instruction, while the 32 bit is 5 bytes?
Because "mov eax,42" is a dword instruction (because of the dword register eax). An instruction can distinguish between byte and word only - in 16 bit mode a word is a word (16 bit), in 32 bit mode, a word is a dword (32 bit). So if you want to use dword instructions in 16 bit mode or word instructions in 32 bit mode, you put an 0x66 in front of it. Hence 0x66 0xB8 means "move the following 32 bit constant value to accumulator (eax)" in 16 bit mode and "move the following 16 bit constant value to accumulator (ax)" in 32 bit mode. And because of that, the CPU reads two bytes instruction and then four bytes (32 bit) data in 16 bit mode, but two bytes (16 bit) data in 32 bit mode. So there are two bytes left in 32 bit mode: 0x00 0x00. These mean "mov [eax],al".
If you want to use a dword instruction in 32 bit mode, you don't need the 0x66 prefix - that's why the 32 bit code is one byte shorter.
holypanl wrote:And isn't 5 bytes a bit...odd? From every source I've read so far, the CPU picks up instructions in its WORD size.
Really? I don't think so, as far as I know, it picks up the instructions in bytes. If you take a hex editor, you can see that non optimized assembler code is not aligned. (Compilers sometimes use "nop" bytes (0x90) to pad loops or something like that)
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 1:54 pm
by gravaera
Right! thanks. I get it now. So the opcode byte is followed by the four other bytes because each operand is word sized. So interesting! I MUST get a job in embedded systems, or in a mainstream OS company. I honestly wouldn't mind working for MicroSoft, honestly, as long as I get into one of the core teams. That would be AWESOME.
For now, though: I have to go learn off some Structured Programming Methodology diagrams, which Universities teach, which you never use in real life. Thanks a lot!
Re: Changing VESA resolution in Long/Pmode
Posted: Tue Aug 04, 2009 5:15 pm
by Combuster
To complete the original question: you can not use any bios code directly from long mode. You have to emulate/recompile it. For the ones that are in protected rather than long mode, V8086 mode is available as the built-in emulator.
The reason for this emulation step is not the instructions: both long and protected mode can execute in 16 bits. However, both use protected segmentation. When you alter a segment register in real mode, the CPU calculates the base field for you. In protected mode, it is looked up from the GDT, and in long mode it is not even possible to change the base for the common segment registers.