Switching to realmode for a BIOS interrupt, sometimes hangs
Switching to realmode for a BIOS interrupt, sometimes hangs
Hello,
I'm not that much of a fan of V86 mode, so I've decided that in my kernel when I need to do a real mode interrupt (which is rare) I'll just switch to real mode; do the interrupt and jump back to protected mode.
The implementation is pretty simple, I keep a relocateable piece of real mode code that I can properly patch (the relative or absolute offsets) and relocate to any real mode memory address.
This works, mostly
Since I can control the place of relocation and it's the first thing I use in the low memory area (<1mb) I have noticed that calling the BIOS (int 0x15) for extended memory information can sometimes hang if the routine is located in certain memory areas: since I know the free low memory theoretically start at 0x500 and end at 0xA0000, I should have it free, everything works fine if the routine runs from 0x500, but hangs if the routine runs for example from 0x800 - which should be fine since this is a free memory area.
This made me think if perhaps the bios interrupts expects certain things which I am maybe not setting correctly, I have loaded all the segments with valid 16bit descriptors, set a real mode IDT, a real mode stack (which is derived from the location the routine is located and is around 1k) patched all the correct addresses, reampped the PIC back to normal, even with interrupts disabled it hangs..
Furthermore, if I force the INT 0x15 to be my own real mode interrupt vector it always works, no matter where it's located, and I have also debugged that the registers are transfered correctly to-and-from protected mode to real mode.
[IE the real mode stack does its' job]
I know this is a longshot, but if anyone have any idea what may be preventing the bios from functioning correctly when I use certain addresses it would be much helpful.
Thanks in advance,
Daniel.
* I use Microsoft Virtual PC 2004 [which emulates BIOS].
* PS: This might be lame of me but I was pretty surprised to find out that you must set CS to something different than 0 in real mode in order to run code. Since you can describe the address 0x500 with either: CS:0 and offset 0x500 or CS:50 and offset 0.
I'm not that much of a fan of V86 mode, so I've decided that in my kernel when I need to do a real mode interrupt (which is rare) I'll just switch to real mode; do the interrupt and jump back to protected mode.
The implementation is pretty simple, I keep a relocateable piece of real mode code that I can properly patch (the relative or absolute offsets) and relocate to any real mode memory address.
This works, mostly
Since I can control the place of relocation and it's the first thing I use in the low memory area (<1mb) I have noticed that calling the BIOS (int 0x15) for extended memory information can sometimes hang if the routine is located in certain memory areas: since I know the free low memory theoretically start at 0x500 and end at 0xA0000, I should have it free, everything works fine if the routine runs from 0x500, but hangs if the routine runs for example from 0x800 - which should be fine since this is a free memory area.
This made me think if perhaps the bios interrupts expects certain things which I am maybe not setting correctly, I have loaded all the segments with valid 16bit descriptors, set a real mode IDT, a real mode stack (which is derived from the location the routine is located and is around 1k) patched all the correct addresses, reampped the PIC back to normal, even with interrupts disabled it hangs..
Furthermore, if I force the INT 0x15 to be my own real mode interrupt vector it always works, no matter where it's located, and I have also debugged that the registers are transfered correctly to-and-from protected mode to real mode.
[IE the real mode stack does its' job]
I know this is a longshot, but if anyone have any idea what may be preventing the bios from functioning correctly when I use certain addresses it would be much helpful.
Thanks in advance,
Daniel.
* I use Microsoft Virtual PC 2004 [which emulates BIOS].
* PS: This might be lame of me but I was pretty surprised to find out that you must set CS to something different than 0 in real mode in order to run code. Since you can describe the address 0x500 with either: CS:0 and offset 0x500 or CS:50 and offset 0.
Re:Switching to realmode for a BIOS interrupt, sometimes han
Actually there is the Extended BIOS Data Area in 0x90000, it's size varies so you need to use the available conventional memory function first. I don't see what exactly you may have done to the BIOS, are you relocating the data references as well? It may be helpful if you post the the relocatable code chunk.
Have you tried it in Bochs? On real hardware?
Have you tried it in Bochs? On real hardware?
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switching to realmode for a BIOS interrupt, sometimes han
parts of int 15h in the BIOS do switch between real and protected mode (for instance the 'high memory copy' service). I'm not sure for the service you're exactly looking for, but that might be a clue.
did you managed to do simpler things such as printing characters, etc from the realmode ?
did you managed to do simpler things such as printing characters, etc from the realmode ?
Re:Switching to realmode for a BIOS interrupt, sometimes han
Data references are non existent and the code was specially written to use only near references so it does not care about absolute addresses. (Except from the protected mode reentry jump point). Everything is done with the real mode stack which is initialized to a known real mode address. (Reloc point + RoutineCodeSz + 1k)
Even so, the routine is merely couple of bytes + a 1k real mode stack that can not overwrite any important EBIOS areas or so.
Writing to the text screen memory for chars works, always, when I run the kernel from DOS mode and am not overriding dos addresses I can run DOS int 0x21 routines as well.
INT 0x21 has the same problem if it's relocated to 0x800 as well, I might be missing something out. (0x500, 0x10000 works).
( I didn't test the BIOS for writing chars ).
INT 0x11 always works.
Again, if I override any of these interrupts with my own routines, (that are relocated as well) it always works, no matter in what address which may imply that I'm not setting something correctly.
The bios service that I'm running is E801h which gets the extended memory amount (>64mb support) to AX and BX.
Am I missing something here?
Segments have all valid real mode descriptors and are reset when entering real mode, stack is correctly set (debugged), IDT is set back to a real mode IDT, paging is off (Not even used), eflags are reset, IRQS might be on or off and the PIC remapped.
There is something wrong here that it works with certain addresses and does not work from other addresses.
I have not tried it with Bochs or on real hardware..
Thanks for all your help, if you have any more pointers it would be most helpful.
Even so, the routine is merely couple of bytes + a 1k real mode stack that can not overwrite any important EBIOS areas or so.
Writing to the text screen memory for chars works, always, when I run the kernel from DOS mode and am not overriding dos addresses I can run DOS int 0x21 routines as well.
INT 0x21 has the same problem if it's relocated to 0x800 as well, I might be missing something out. (0x500, 0x10000 works).
( I didn't test the BIOS for writing chars ).
INT 0x11 always works.
Again, if I override any of these interrupts with my own routines, (that are relocated as well) it always works, no matter in what address which may imply that I'm not setting something correctly.
The bios service that I'm running is E801h which gets the extended memory amount (>64mb support) to AX and BX.
Am I missing something here?
Segments have all valid real mode descriptors and are reset when entering real mode, stack is correctly set (debugged), IDT is set back to a real mode IDT, paging is off (Not even used), eflags are reset, IRQS might be on or off and the PIC remapped.
There is something wrong here that it works with certain addresses and does not work from other addresses.
I have not tried it with Bochs or on real hardware..
Thanks for all your help, if you have any more pointers it would be most helpful.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switching to realmode for a BIOS interrupt, sometimes han
well, it looks like 0x800 would be safe indeed, unless someone else put something important there ... i don't think that could be the BIOS (but you could be running "get memory map" before you load your kernel and check that assumption)
Maybe that could be DOS or maybe that could be bootloader or something alike.
E.g. if you accidently overwrite your GDT while in realmode and simply try to return to protected mode, BadThings (tm) will occur. Same for other important structures.
Did you tried to print a char in real mode after calling the BIOS service you're interrested in to check whether the BIOS or your return process is what's hanging the system ?
Maybe that could be DOS or maybe that could be bootloader or something alike.
E.g. if you accidently overwrite your GDT while in realmode and simply try to return to protected mode, BadThings (tm) will occur. Same for other important structures.
Did you tried to print a char in real mode after calling the BIOS service you're interrested in to check whether the BIOS or your return process is what's hanging the system ?
Re:Switching to realmode for a BIOS interrupt, sometimes han
The kernel boots from DOS [without himem and emm386], however even with DOS loaded and since I do not use its services 0x800 should be free.
When I say hangs, it ALWAYS occur in the INT instruction, probably the interrupt does not return. If I remove the INT instruction and just let the code run (switch to real mem -> back with registers and stack) the code runs fine with all memory configurations which makes me believe my code is ok.
Nevertheless there must be something wrong with it since it prevents BIOS INT 0x15 to work when in 0x800.
Since the return to protected mode always works, (unless the INT instruction stuck) I can "safely" assume that no memory is being overwritten.
(I am also printing chacracters after the INT returns, when it hangs it just does not return).
Again Pype.Clicker thanks for all your help if you ever have any more suggestions I would be glad to hear them.
I guess I'll keep on testing it or just leave it in 0x500
Thanks!
When I say hangs, it ALWAYS occur in the INT instruction, probably the interrupt does not return. If I remove the INT instruction and just let the code run (switch to real mem -> back with registers and stack) the code runs fine with all memory configurations which makes me believe my code is ok.
Nevertheless there must be something wrong with it since it prevents BIOS INT 0x15 to work when in 0x800.
Since the return to protected mode always works, (unless the INT instruction stuck) I can "safely" assume that no memory is being overwritten.
(I am also printing chacracters after the INT returns, when it hangs it just does not return).
Again Pype.Clicker thanks for all your help if you ever have any more suggestions I would be glad to hear them.
I guess I'll keep on testing it or just leave it in 0x500
Thanks!
Re:Switching to realmode for a BIOS interrupt, sometimes han
DOS could be inserting a handler into INT 0x15, when the interceptor is triggered you have overridden it and it has undefined behaviour.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switching to realmode for a BIOS interrupt, sometimes han
Yep. that's what i was about to say myself: it's common for DOS to rehook some interrupt handlers towards its own code, and therefore DOS code might be involved even if you simply call a BIOS function.AR wrote: DOS could be inserting a handler into INT 0x15, when the interceptor is triggered you have overridden it and it has undefined behaviour.
A common technique for memory management was to hook the 'return memory size' BIOS function so that it reports less than available, keeping the amount of memory you don't report for yourself. DOS may be doing this for XMS setup or something alike.
In other words, if you're started from DOS, do not assume a memory region is free unless you allocated it with DOS functions. Any program i wrote that did not follow that rule eventually shown to be broken under some setup (like running from a dosshell, or after smartdrive was launched or whatever)
Re:Switching to realmode for a BIOS interrupt, sometimes han
I had this in mind but only that the DOS components that do so are HIMEM.SYS and EMM386 (HIMEM redirects INT 0x15 to return 0 free extended memory so you'll be forced to use the XMS interface).
Do you believe that even without those services (PLAIN DOS without the extra devices) DOS still redirects the input?
When the BIOS routines work (from address 0x500), I do get the correct amount of extended memory available from the bios routine... (not 0)
Do you think that DOS itself redirects these interrupts even without HIMEM or EMM386?
I could just look at the source code (MSDOS6 source code available from P2P) but I wouldn't bother.
Thanks for all your help, again.
Do you believe that even without those services (PLAIN DOS without the extra devices) DOS still redirects the input?
When the BIOS routines work (from address 0x500), I do get the correct amount of extended memory available from the bios routine... (not 0)
Do you think that DOS itself redirects these interrupts even without HIMEM or EMM386?
I could just look at the source code (MSDOS6 source code available from P2P) but I wouldn't bother.
Thanks for all your help, again.
Re:Switching to realmode for a BIOS interrupt, sometimes han
DOS does it for several things, take INT 0x13 for example, apparently really old (like 15+ years) hardware fails to respond to query commands properly (locks the Bus) so DOS installs an interceptor to manually unlock the bus if necessary. The easiest way to check would be to simply look at the IVT entry for INT 0x15, if it's in the BIOS Area then it hasn't been overridden otherwise DOS has most likely remapped it.
Re:Switching to realmode for a BIOS interrupt, sometimes han
By DOS do you mean MS-DOS or WINDOWS 95+ DOS?
As far as I know, Windows 95+ "DOS" always loads HIMEM.sys whether you want it or not. It's part of the OS - nothing to do with CONFIG.SYS. That means you could be facing problems with DOS demanding the high memory area and all of XMS memory. HIMEM also is responsible for UMB's regardless of EMM386 being loaded or not. EMM386 is only the driver.
If this is the case, grab a copy of DR DOS or MSDOS 5/6 (I use all three on occasion) and see if the problem still occurs.
Freedos is also available if you want to escape commercial all together.
The only other thing I can think of is if you reload the GDT when switching back into protected mode. the 16 bit values in your segment registers may be pointing elsewhere in the GDT, a problem that doesn't happen in VM86 mode or protected mode interrupts.
As far as I know, Windows 95+ "DOS" always loads HIMEM.sys whether you want it or not. It's part of the OS - nothing to do with CONFIG.SYS. That means you could be facing problems with DOS demanding the high memory area and all of XMS memory. HIMEM also is responsible for UMB's regardless of EMM386 being loaded or not. EMM386 is only the driver.
If this is the case, grab a copy of DR DOS or MSDOS 5/6 (I use all three on occasion) and see if the problem still occurs.
Freedos is also available if you want to escape commercial all together.
The only other thing I can think of is if you reload the GDT when switching back into protected mode. the 16 bit values in your segment registers may be pointing elsewhere in the GDT, a problem that doesn't happen in VM86 mode or protected mode interrupts.
Re:Switching to realmode for a BIOS interrupt, sometimes han
Thanks everyone for all your help.
After some testing I've found that DOS (using MS-DOS71) indeed patches up almost all the interrupt vectors to his own versions, even with no drivers loaded (clean DOS with just command.com).
So I'm just collecting the REAL bios INT vectors addresses and patch them up when I start my kernel, this works. ( I had to do it manually for each interrupt I was interested on, to make my kernel run from DOS as well. )
By the way does anyone know if there is an AUTOMATIC way to get from the BIOS the memory addresses of the BIOS' original interrupt vectors (set when the computer boots) ?
It must be stored somewhere, or perhaps its just hardcoded addresses in the bios startup instructions.
Thanks again for all your help.
* Xardfir: Tried FreeDOS, with a clean boot it indeed maintains almost the complete BIOS interrupt list (unlike DOS71), however it had some compatibility problems with running my loader in UNREAL mode. (quit with INVALID opcode).
After some testing I've found that DOS (using MS-DOS71) indeed patches up almost all the interrupt vectors to his own versions, even with no drivers loaded (clean DOS with just command.com).
So I'm just collecting the REAL bios INT vectors addresses and patch them up when I start my kernel, this works. ( I had to do it manually for each interrupt I was interested on, to make my kernel run from DOS as well. )
By the way does anyone know if there is an AUTOMATIC way to get from the BIOS the memory addresses of the BIOS' original interrupt vectors (set when the computer boots) ?
It must be stored somewhere, or perhaps its just hardcoded addresses in the bios startup instructions.
Thanks again for all your help.
* Xardfir: Tried FreeDOS, with a clean boot it indeed maintains almost the complete BIOS interrupt list (unlike DOS71), however it had some compatibility problems with running my loader in UNREAL mode. (quit with INVALID opcode).
Re:Switching to realmode for a BIOS interrupt, sometimes han
You can try searching Ralf Brown's Interrupt List but I don't recall seeing anything like it. The only way I can think of is far jumping to 0xFFFF:0x0 causing the computer to soft reboot.
Then again, I think I read somewhere that some of the BIOS interrupt routines are at known offsets, can't remember where I saw the list, and have even less of an idea if it would be robust to rely on it.
Then again, I think I read somewhere that some of the BIOS interrupt routines are at known offsets, can't remember where I saw the list, and have even less of an idea if it would be robust to rely on it.
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:Switching to realmode for a BIOS interrupt, sometimes han
I'm not so sure you'll find it: even if there's an unpacked copy of the int table in the BIOS ROM, you'll have hard time when moving to another BIOS which may store it elsewhere, etc.Trashey wrote: By the way does anyone know if there is an AUTOMATIC way to get from the BIOS the memory addresses of the BIOS' original interrupt vectors (set when the computer boots) ?
Really, if you're writing a loader-from-DOS, you should ask DOS for the memory you're going to use. That will gently fix all the further coming problems you might experience.
Re:Switching to realmode for a BIOS interrupt, sometimes han
This was just out of curiosity question, loading from DOS is just a matter of development convenience for me and in all other situations the boot loader is used.
The entire idea is that the kernel could load from DOS without paying attention that it has been loaded from DOS during run time. Which after I patch the interrupt vectors, is exactly the situation.
I was just wondering if there is a universal address for it that span most BIOSes, probably not.
Thanks
The entire idea is that the kernel could load from DOS without paying attention that it has been loaded from DOS during run time. Which after I patch the interrupt vectors, is exactly the situation.
I was just wondering if there is a universal address for it that span most BIOSes, probably not.
Thanks