OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 2:53 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: returning from v8086 mode
PostPosted: Mon Sep 16, 2019 1:22 am 
Offline

Joined: Mon Sep 16, 2019 12:57 am
Posts: 5
Hi all,
I have been experimenting with using v8086 mode for all the BIOS calls of a little bootloader I'm writing, so that I can avoid writing any assembly by hand as much as possible. In fact, right now I have only a 70 lines asm file which literally just loads a track from the disk and sets up protected mode, and everything else, including mode setting and all that, is in C.
When my code enters v8086, I set up a little stack and I have a GP handler that intercepts all the so-called "sensitive" instructions, including iret, and then iret myself into a BIOS interrupt handler. So when I get an iret in v8086 and I see that the stack is about to underflow, I take that as meaning that the BIOS handler is done, and simply return to the caller in protected mode.
This worked perfectly on bochs and qemu, but failed on my laptop (Thinkpad x280), where I got an "Invalid Opcode" exception when returning from the BIOS call to get the drive geometry. After some investigation, it turned out that the code was jumping to address 0, for some reason. It took me forever to diagnose (booting a real machine is not as snappy as booting qemu, plus I can't just probe memory however I want), but eventually I figured it out, and I thought I might post it here to possibly help other people in a similar situation.
The problem is that, for whatever reason, the BIOS on that machine decided to return from the handler with a far ret instead of iret. Since I don't (and can't) intercept ret instructions, that one would just do its thing on its own, which would be popping invalid data after the base of the stack, which happened to be 0, and jump there.
Once I figured this out, it was trivial to fix: I added a 5 byte stack guard contaning an iret opcode and its far address, so now when this happens, the BIOS handler will jump to my iret instruction, and from there back to my GP handler, which will exit v8086 mode.
Is there any reason why that BIOS code would prefer to emulate an iret using ret? It didn't even occur to me that one would think to do that, and that's what made this debugging process so painful. Note that other BIOS routines worked as expected, so it seems to be a peculiarity of this one for the drive geometry.
Thanks!


Top
 Profile  
 
 Post subject: Re: returning from v8086 mode
PostPosted: Mon Sep 16, 2019 8:58 am 
Online
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
I feel reminded of something Solar once said: A sane BIOS implementation is like a unicorn. You can't prove it doesn't exist, and from time to time you will encounter something that sort of looks like it, but in the end it will just turn out to be a horse again.

However, BIOS can be implemented in any way the vendor wants, so long as the contract is met and you can't really tell the difference. One reason for choosing retf over iret might be because many BIOS calls return an error with the carry flag, and it might be easier to pass the carry flag from the actual function to the caller in the flags register. iret would clobber flags with the image on stack. retf doesn't. (Though that would mean this BIOS clobbers IF. Unless it checks if that flag is set in the flags image on the stack. But that would defeat the purpose...)

You know, probably the easiest implementation would be to have a special interrupt that exits your VM monitor. Say interrupt 255. Then you could just run a little program in v8086 mode:
Code:
mov ax, whatever
mov bx, whatever
mov cx, whatever
mov dx, whatever
int whatever
int 255

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: returning from v8086 mode
PostPosted: Mon Sep 16, 2019 10:11 am 
Offline
Member
Member

Joined: Tue Mar 04, 2014 5:27 am
Posts: 1108
nullplan wrote:
You know, probably the easiest implementation would be to have a special interrupt that exits your VM monitor. Say interrupt 255. Then you could just run a little program in v8086 mode:
Code:
mov ax, whatever
mov bx, whatever
mov cx, whatever
mov dx, whatever
int whatever
int 255


Anything that causes an exception and is handled in kernel mode will work (even division by 0). But the cleanest is probably to also check CS:(E)IP of that special "offending" instruction.


Top
 Profile  
 
 Post subject: Re: returning from v8086 mode
PostPosted: Mon Sep 16, 2019 5:28 pm 
Offline
Member
Member

Joined: Wed Mar 09, 2011 3:55 am
Posts: 509
unablegrid wrote:
When my code enters v8086, I set up a little stack and I have a GP handler that intercepts all the so-called "sensitive" instructions, including iret, and then iret myself into a BIOS interrupt handler. So when I get an iret in v8086 and I see that the stack is about to underflow, I take that as meaning that the BIOS handler is done, and simply return to the caller in protected mode.


This is clever, but probably too clever.

As nullplan said, it's probably better to have your own code that runs in v86 mode for your protected mode code to transfer control to on entering v86 mode, and to have that code call the BIOS routine. When the BIOS routine finishes, your trampoline code can then trap back to protected mode by a method that's under your control, and thus guaranteed to actually trap.


Top
 Profile  
 
 Post subject: Re: returning from v8086 mode
PostPosted: Mon Sep 16, 2019 10:16 pm 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 671
Since you are using v8086 to make BIOS calls what I say may be overkill below because you have the ability to hard code a trampoline in the v8086 memory space, place the stack at places that are well known to you etc. If however you had been running an OS (like DOS or any arbitrary third party code) in v8086 mode then you would have to consider other options. Placing the trampoline on the stack would be one.

If you had been running arbitrary code that you don't control that issued an Int 0x13 (or any Int N) and you wanted to have a corresponding IRET to trap your v86 monitor whether IRET or a FAR RETURN is used you could build the trampoline code on the stack to do the IRET for you.

Your monitor is trapping the `int n` instruction already. You likely created a real mode stack frame with 6 bytes to emulate what `int n` does. That would be the original flags at the point of the `int n` instruction (probably with IF and TF turned off), the CS at the point of the `int n` instruction and the IP of the `int n` instruction PLUS 2 (to return to the instruction after the `int n`). Then you returned from the v86 monitor with the real mode CS:IP pointing to the address of the interrupt routine as read from the real-mode IVT so the software interrupt would occur.

What you could have done is put additional bytes on the stack (the trampoline) that issues the IRET for you. When doing `int n` you could have pushed onto the real mode stack these items so they had this order:

CS - The original CS pushed on the stack by processor when v8086 was interrupted (WORD)
IP - The original IP+2 (Int n is 2 byte instruction) pushed on the stack by processor when v8086 was interrupted
Magic - a 16-bit magic value (this is optional but acts as another check and could be expanded to a DWORD)
0x90 - any BYTE for padding but I'll choose a NOP (keep stack evenly aligned)
0xCF - an IRET opcode (BYTE)
Flags - pushed on stack by processor when v8086 was interrupted probably with TF=0 and IF=0 (WORD)
SS - value of SS pushed by the processor when the v8086 was interrupted (WORD)
Offset - real-mode stack offset of the IRET opcode in this trampoline (WORD)

The last 3 items are somewhat familiar because they are a typical interrupt frame with FLAGS/CS/IP with the exception that they don't return directly to the original CS:IP that caused the interrupt, but to the offset right on the stack where the IRET in the trampoline is. Whether reached by a FAR RETURN or an IRET, the IRET in the trampoline will be called. That will trap into your v8086 monitor. The only catch is that you would have two IRETs trap back to the monitor. This can be potentially avoided if your normal IRET processing is adjusted to look for another IRET at SS:SP+6 and the Magic number on the stack at SS:SP+8. If they are present then you can retrieve the CS:IP from the trampoline to return to, adjust the stack pointer including removal of the trampoline and avoid the unnecessary second IRET.

Now the problem is - how do you distinguish between a special trampoline and what would be a normal interrupt frame in you v8086 monitor? The first thing you can do after determining that an IRET caused the trap from v8086 mode is check to see if the CS:IP of the instruction is also equal to SS:SP. You can then also check if there is a magic value on the stack at SS:SP+2 (optional idea). If you detect this isn't a trampolined IRET do your normal IRET processing otherwise do something like:

Retrieve the FLAGS pushed on the stack when the processor faulted on the IRET instruction
Retrieve IP from real mode stack at SS:SP+4
Retrieve CS from real mode SS:SP+6

Adjust the real mode stack SP by adding 8 thus removing the trampoline from the stack (The IRET Opcode; the 0x90 padding byte, Magic value; CS and IP).

Then just return from the v8086 monitor back to v8086 mode with the flags, and the CS:IP retrieved from the trampoline.


Top
 Profile  
 
 Post subject: Re: returning from v8086 mode
PostPosted: Tue Sep 17, 2019 3:48 am 
Offline

Joined: Mon Sep 16, 2019 12:57 am
Posts: 5
Quote:
This is clever, but probably too clever.

As nullplan said, it's probably better to have your own code that runs in v86 mode for your protected mode code to transfer control to on entering v86 mode, and to have that code call the BIOS routine. When the BIOS routine finishes, your trampoline code can then trap back to protected mode by a method that's under your control, and thus guaranteed to actually trap.


I agree, I was just looking for a way that minimised the amount of assembly I had to write. And ultimately, if you think about it, it's sort of what I'm doing anyway: I am manually creating a real mode stack frame that looks exactly as if I had issued an int instruction in real mode.

However, thinking about it some more, it is probably a good idea to dedicate an interrupt to exiting v8086, instead of checking for stack underflow like I'm doing. I suppose some BIOS routine could (on purpose or not) mess around with the stack temporarily, and if they happen to issue an iret while the stack has been moved somewhere else, the v8086 manager could mistake that for underflow. Using a dedicated interrupt would solve that.

I'm still digesting MichaelPetch's suggestion. :)

Thanks for the input!


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: Majestic-12 [Bot], nullplan and 55 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