Page 1 of 2

Back to real mode [SOLVED]

Posted: Tue Apr 19, 2011 10:39 am
by Karlosoft
Hi I need to switch back to real mode in order to perform some software interrupt operations and run old code. In order to do this I wrote a routine which should

- Store the current status of machine
- Load the old real mode data saved before this function
- Go back in real mode, perform the operation
- Enable again protected mode and jump back to my C++ code.

In order to do this, I saved the first 0x400 bytes of memory at start

Code: Select all

memcpy((void*)0x40000, (void*)0x0, 0x400);
&I saved the old pic masks

Code: Select all

bios_mask[0]= inportb(0x21);                       
bios_mask[1] = inportb(0xa1);

Than, I disabled the interrupts with cli, I also remapped the pic and loaded the old bios values

Code: Select all

irq_remap(0x08,0x70);
outportb(0x21, bios_mask[0]);
outportb(0xA1, bios_mask[1]);
Finally I performed some memory copies to save the current first 0x400 bytes and to load the old ones.

Code: Select all

memcpy((void*)0x40400, (void*)0x0, 0x400);
memcpy((void*)0x0, (void*)0x40000, 0x400);
And than a call

Code: Select all

p_int86(&gp); 
The code of p_int86 is this

Code: Select all

org 0x0

; It is loaded in 0x40000
[bits 32]
TIMES 0x800 db 0

mov eax, cr0
mov [savcr0+0x40000], eax	; save pmode CR0
cli			; Disable interrupts.
lidt [idt_real+0x40000]
jmp dword 0x18:(start+0x40000)

idt_real:
	dw 0x3ff		; 256 entries, 4b each = 1K
	dd 0			; Real Mode IVT @ 0x0000
 
savcr0:
	dd 0			; Storage location for pmode CR0.
 

 

start:	

; We are already in 16-bit mode here!
[bits 16]

; Need 16-bit Protected Mode GDT entries!
mov eax, 0x20	; 16-bit Protected Mode data selector.
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
 
; Disable paging (we need everything to be 1:1 mapped).
mov eax, cr0
and eax, 0x7FFFFFFe	; Disable paging bit & enable rmode.
mov cr0, eax

jmp 0x4000:GoRMode		; Perform Far jump to set CS.


GoRMode:
mov ax, 0x4000		; Reset segment registers to 0x4000.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
	

mov eax, esp
and eax,0xf0000
shr eax,4
mov ss, ax

mov eax, esp
and eax, 0xffff
mov sp, ax
	

sti
mov ebx,0
mov eax,0

mov ah,0x0
mov al,0
int 0x10

mov ah,0xe
mov al, '4'
mov bh,0
mov bl, 42
int 10h

hlt
The code ends before I return in protected mode because it isn't important. All the operation after sti should be the code to perform in real mode. But it doesn't work well

On my old computer it works but I need to make a cli very early or it restarts. This happens only if I remap the pic, otherwise it works good.
In my new computer It doesn't restart, it doesn't work (the interrupts I mean because I tried simple code to emit a beep in real mode code and it works).

Have I missed anything? What could be wrong? Perhaps I forgot to set up something?

Oh yes this is my gdt

Code: Select all

gdt_set_gate(0, 0, 0, 0, 0);
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
gdt_set_gate(3, 0, 0xFFFFF, 0x9a, 0x00);        //16bit code
gdt_set_gate(4, 0, 0xFFFFF, 0x92, 0x00);        //16bit data

Re: Back to real mode

Posted: Tue Apr 19, 2011 4:15 pm
by DavidCooper
Karlosoft wrote:In order to do this, I saved the first 0x400 bytes of memory at start

Code: Select all

memcpy((void*)0x40000, (void*)0x0, 0x400);
Why not the first 0x500? Is there no danger of you accidentally changing the BDA?
mov eax, cr0
and eax, 0x7FFFFFFe ; Disable paging bit & enable 16-bit pmode.
mov cr0, eax
Comment wrong - you're enabling real mode.
GoRMode:
mov ax, 0x4000 ; Reset segment registers to 0.
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
Comment wrong - you're setting them to 0x4000.
mov eax, esp
and eax,0xf0000
shr eax,4
mov ss, ax

mov eax, esp
and eax, 0xffff
mov sp, ax
Is your stack guaranteed to be in the bottom megabyte before this runs? By the way, it might be worth making BP equal to SP just before calling the BIOS - I've had problems with a recent BIOS that can't handle BP being a long way in value from SP.
On my old computer it works but I need to make a cli very early or it restarts. This happens only if I remap the pic, otherwise it works good.
That sounds like a clue. What happens in your new computer if you haven't remapped the pic? Your code never really worked properly on the old computer if you had to rush to run a cli, so that's a problem you need to understand.

Re: Back to real mode

Posted: Tue Apr 19, 2011 10:07 pm
by Brendan
Hi,
Karlosoft wrote:Hi I need to switch back to real mode in order to perform some software interrupt operations and run old code.
A good OS will:
  • Start any/all extra CPUs
  • Mess with certain MSRs in all CPUs, including the MTRRs, those for SYSCALL/SYSENTER, etc.
  • Enable IO APICs and x2APIC (where present)
  • Reset and reconfigure the state of every single device, potentially including messing with each device's PCI configuration space
  • Setup timers for certain things (HPET, local APIC timers, etc)
  • Respond to IRQs (and IPIs from other CPUs) in a timely manner: typically you don't want to disable IRQs for more than about 10 instructions if you can avoid it
  • Allow transfers (e.g. DMA transfers and PCI bus mastering) to occur while everything else is going on
  • Enable "ACPI mode" for power management; and reclaim any "ACPI reclaimable" memory
  • Support EFI/UEFI
During boot (e.g. before anything above is done) you can run obsolete real mode code and don't need to restore the state of anything. After that it's too late to run real mode code and you've only got 3 sane options:
  • Use virtual8086 and/or a software emulator to run the obsolete real mode code in some sort of virtual machine
  • Reboot the computer and run the obsolete real mode code during the next boot
  • Write native code to replace the obsolete real mode code
Of course it's possible that your OS is "considerably simple", especially if it's in the very early stages of development and the amount of stuff it uses and reconfigures isn't very much. This can create the illusion that switching to real mode might be sane idea; but as you improve the OS (e.g. start supporting SMP, start writing lots of device drivers, etc) it becomes harder and harder to maintain the illusion.

Basically you've got 2 problems: (potential) bugs in the code you posted, and the reason for the code you posted to exist. Once you fix the latter problem the former problem disappears... ;)


Cheers,

Brendan

Re: Back to real mode

Posted: Wed Apr 20, 2011 5:50 am
by Owen
An advantage of the emulator method, of course, is that your OS remains in control: You can emulate the legacy peripherals which your OS has reconfigured and which the BIOS is likely to access. One possibility is to initialize your interrupt mapping so the 16 IRQs which connect to the PICs will, if no native handler is registered, invoke the BIOS emulator. When the BIOS emulator then interacts with the PICs (e.g. to send an EOI), you can translate this back to your own OS' interrupt model (So, for example, the BIOS need never know that you're actually using the IO APIC)

Re: Back to real mode

Posted: Wed Apr 20, 2011 7:52 am
by Karlosoft
Sorry my code is right, I kept the old wrong comments from the wiki code. The stack is safe because the base is set before the EBDA memory zone. So for example
esp = 0x845aa
ss= esp & 0xf0000 / 0x10
sp= esp & 0xffff
Isn't right?
The code worked in my old computer if I didn't remap the pic. If I remaps it I need to set a cli or it fails. On my new computer and on the emulator BIOS interrupts doesn't work at all.
The BDA is still the same I protected that area from alloc et similia routines, and I tried to check the values which are still the same they where in the bootloader.
I know that a modern operating system shouldn't do this but I need it like a temporary way before I write a complete emulator. I'm still in a sort of bootloader environment even if in pmode because my kernel is setting up the programs to load, the hardware and so on.

I hope I answered to all the questions and the points. Thank you for the help

Re: Back to real mode

Posted: Wed Apr 20, 2011 11:53 am
by Karlosoft
Fixed on my old computer, not it works well. On my other computers it continues to give problems. The addresses of the ivt aren't called (I tested the ALT-CTRL-CANC combination too...)

Re: Back to real mode

Posted: Wed Apr 20, 2011 12:09 pm
by rdos
Owen wrote:An advantage of the emulator method, of course, is that your OS remains in control: You can emulate the legacy peripherals which your OS has reconfigured and which the BIOS is likely to access. One possibility is to initialize your interrupt mapping so the 16 IRQs which connect to the PICs will, if no native handler is registered, invoke the BIOS emulator. When the BIOS emulator then interacts with the PICs (e.g. to send an EOI), you can translate this back to your own OS' interrupt model (So, for example, the BIOS need never know that you're actually using the IO APIC)
This is the ideal model, but it is very complex and time-consuming. This was basically what DOS-extenders tried to do (in conjunction with an emulator in Windows). In my experience, if you only want to use BIOS for mode-switching, it is perfectly possible to let the BIOS do whatever it wants, as the IO interface for switching video-mode would not be used in the OS kernel anyway. It won't depend on IRQs either (no video-card I've tested with do).

Re: Back to real mode

Posted: Wed Apr 20, 2011 2:00 pm
by Karlosoft
One of the reasons for the which I need it is the USB support to read and write on a pen drive because my newer pc doesn't have any floppy drive...

Re: Back to real mode

Posted: Wed Apr 20, 2011 2:09 pm
by rdos
Brendan wrote:[*]Respond to IRQs (and IPIs from other CPUs) in a timely manner: typically you don't want to disable IRQs for more than about 10 instructions if you can avoid it
I wonder if this is possible on a multitasking, SMP-system? I've always had this as a goal, but I suspect it is more like 100 instructions than 10 in the worse case in the scheduler. This worse case is when the context of the new task is loaded, something that I have not managed to do with interrupts enabled. With SMP this get even worse, as the locking and potentially signalling of other cores also must be done prior to this, and with interrupts disabled. Maybe I'll be able to fix this, but I doubt it.

Re: Back to real mode

Posted: Wed Apr 20, 2011 3:34 pm
by Combuster
Karlosoft wrote:One of the reasons for the which I need it is the USB support to read and write on a pen drive because my newer pc doesn't have any floppy drive...
And the BIOS might have lost valuable USB interrupts by the double reset of the PIC, and decides to keep waiting for The One... (or some other scenario caused by excess latency or changing system devices.)

You'll have to go figure out which action (which may include doing nothing for a while) causes the exact breakage of the USB subsystem, which is probably going to require a good supply of caffeine - a test case would involve rebuilding your OS in parts until you can pinpoint the exact cause.
I wonder if this is possible on a multitasking, SMP-system?
Yes, the only problem you need to avoid is reentering the scheduler via an interrupt. (a solution would be to arbitrate the choice and have the scheduler check if the task switched to is the actual task wanted)

Re: Back to real mode

Posted: Wed Apr 20, 2011 7:55 pm
by DavidCooper
You're going to have to do what I did to solve a recent problem which also involved switching back to real mode to use the BIOS. Step 1 is to write a test routine to switch to protected mode and then return to real mode without doing anything else, then see if the problem occurs at that point (and you should run this code right at the start before you've attempted to set up your OS). Step two, add code to change the pic and then to change it back again before the return to real mode, and see if the problem occurs now. Next, load your protected mode interrupt table as well, then load the real mode one back in and return to real mode. Etc.

Once you've replicated all the changes in this test routine which your OS makes and found that the problem doesn't occur (and hopefully it won't), you need to write an inverse form of it, putting the switch to real mode in first, followed by the switch back to protected mode. This new version of the test routine can be called from many different places within your OS while in protected mode, and you can try it in different places until the problem shows up. You may have to add more functionality or remove some to run it in certain places if the OS hasn't got round to setting up part of it at the point you're testing. By doing this you should be able to pin down the exact point where the problem is caused. Good luck with it - you may have to modify and run your OS a hundred times, but it'll be worth the effort.

Re: Back to real mode

Posted: Thu Apr 21, 2011 1:15 am
by rdos
Combuster wrote:Yes, the only problem you need to avoid is reentering the scheduler via an interrupt. (a solution would be to arbitrate the choice and have the scheduler check if the task switched to is the actual task wanted)
But you always need to set some status that the task-switch is completed. This requires using some registers, which needs to be saved/restored (a waste). In my design, there might also have occured IRQs that woke-up some high-priority thread that should run instead of the current thread. Therefore, after disabling ints before doing the switch to the new task, there is a need to check that the thread about to run is the highest priority thread. That is why I've changed the design so it loads all the registers with ints disabled instead. It is much simpler, and faster most of the time. But it does disable ints longer than might be necesary.

Re: Back to real mode

Posted: Fri Apr 22, 2011 6:45 am
by turdus
Your code is wrong. You execute real mode instructions in pmode. No wonder it doesn't work.
You jump to "start" which contains 16 bit code BEFORE you switch to real mode.
And it's hard to belive that your assembler assemble this:

Code: Select all

mov ds, eax
should be

Code: Select all

mov ds, ax
segment registers are 16 bit registers even in pmode and in longmode too.

Re: Back to real mode

Posted: Fri Apr 22, 2011 6:50 am
by gerryg400
Not sure what you mean. It's entirely possible to execute 16 bit instructions in pmode. As long as the code segment is 16-bit.

Re: Back to real mode

Posted: Fri Apr 22, 2011 8:53 am
by Brendan
Hi,
turdus wrote:And it's hard to belive that your assembler assemble this:

Code: Select all

mov ds, eax
should be

Code: Select all

mov ds, ax
segment registers are 16 bit registers even in pmode and in longmode too.
Actually, because segment registers are 16-bit, for "mov ds,eax" the CPU only uses AX anyway, and Intel say that "mov ds,eax" is better/faster for 32-bit code because it avoids the need for the size override prefix.

If you wrote "mov ds,ax" in 32-bit code, then a good optimising assembler should actually generate "mov ds,eax" instead. Unfortunately most assemblers don't/won't do those sorts of optimisations for you, so you have to do it yourself.


Cheers,

Brendan