Page 1 of 1
problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 7:14 am
by osmosys
i am not able to switch from protected mode to real mode. i have given the code region for this below. the problem is that nothing gets displayed on the screen when i try to print 'A'. if i try to jump to some other location in " ljmp $0,$realmode", it causes qemu to crash and report me the value of registers. i can see that last bit of cr0 has become 0 which means that i have entered protected mode. but did i forget to do anything ??
Code: Select all
cli
lidt rmidt
xor %eax,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
.code16
movl %cr0,%eax
andl $0xfffffffe,%eax
movl %eax,%cr0
DATA32 ljmp $0,$realmode
realmode:
mov $0xb800,%ax
mov %ax,%ds
movb $'A',0
movb $0x1e,1
lo: jmp lo
Re: problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 7:20 am
by CodeCat
You have the .code16 directive in there before the actual switch to real mode. As a result of that, the assembler emits an operand size override that is meant for real mode code, even though the following three instructions are still executed in protected mode. My guess is that this results in the CPU actually executing:
Code: Select all
movl %cr0,%ax
andl $0xfffffffe,%ax
movl %ax,%cr0
Or even worse because it interprets the now-redundant additional 16 bits of the operand 0xFFFF as a separate instruction, which is obviously not going to work.
I'm not sure if that is the problem, but try putting the .code16 directive after the 'movl %eax,%cr0' instruction.
EDIT: I tried assembling it in 16 bit mode and then disassembling in 32 bits mode, and this is what I got (NASM syntax):
Code: Select all
00000000 0F20C0 mov eax,cr0
00000003 6625FEFF and ax,0xfffe
00000007 FF db 0xFF
00000008 FF0F dec dword [edi]
0000000A 22C0 and al,al
0000000C EA11000000B800 jmp dword 0xb8:0x11
00000013 B88ED8C606 mov eax,0x6c6d88e
00000018 0000 add [eax],al
0000001A 41 inc ecx
0000001B C60601 mov byte [esi],0x1
0000001E 001E add [esi],bl
00000020 EBFE jmp short 0x20
Which is obviously completely messed up. The byte 0x66 at the start of the second instruction says 'this instruction's registers are the non-default type'. The assembler adds this because the instruction uses eax, which is not the default size (ax is the default size because of the .code16). However when the CPU actually runs it, it's running in 32 bit mode, so the non-default size is now 16 bits, using ax. As a result, it only actually grabs the first two bytes of the operand, 0xFFFE, and assumes that the following 0xFFFF are part of the next instruction. Then it tries to execute 0xFF which according to NDISASM isn't even a valid instruction, so QEMU is probably dumping registers as a result of an unhandled 'invalid opcode' exception. And even if 0xFF had been a valid instruction, the program would never work, as the instructions are misaligned.
Re: problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 8:10 am
by osmosys
thanks friend for your suggestion
i changed the code to this
Code: Select all
cli
lidt rmidt
xor %eax,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
mov %cr0,%eax
and $0xfffffffe,%eax
mov %eax,%cr0
.code16
ljmp $0,$realmode
realmode:
mov $0xb800,%ax
mov %ax,%ds
movb $'A',0
movb $0x1e,1
lo: jmp lo
but still it doesn't work..if i still i try to jump to some other random location, qemu crashes and it shows that lsb of cr0 is 0. i have couple of questions to ask
(1) is the next instruction after moving new value to cr0 taken as a 16 bit instruction ?
(2) is there some other reliable way to test whether it has reached the jump location correctly other than printing some character on the screen ?
(3) is there a way to output the value of all registers at the time i close qemu (so that i can notice the value of eip and see where the execution is going on) ?
Re: problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 8:26 am
by CodeCat
1) Yes. From the moment the PE bit in cr0 is cleared, the CPU is in real mode.
2) Try putting an infinite loop or a hlt instruction right after the jump target. If it hangs while running rather than crashing, you know it got at least that far. Then you can move the loop/hlt forward one instruction at a time, until you find the one it crashes at.
3) I'm not sure how QEMU works, but Bochs dumps all the registers in its log when you close it. Using the hlt instruction can then allow you to decide what point to stop at to see the registers.
Also, I think this topic would do better in the OS development forum, if it could be moved?
Re: problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 10:13 am
by osmosys
interestingly when i remove the line .code16 it WORKS...
i don't know how, but the instructions get assembled as 32-bit instructions and then it works and prints A
i checked grub's source and found out that instructions .code16 is used there...
could it be a bug with of qemu..i need to test it on a real system...
Re: problem when switching from protected mode to real mode
Posted: Thu Oct 23, 2008 10:37 am
by CodeCat
That's very strange indeed. If you assemble in 32 bits but run in 16 bits, the same thing happens as what I described above, so that can't possibly work. However, if it writes the 'A' to video memory, then that proves that it's using real-mode segmentation for how it interprets the contents of ds. So it really is in real mode, yet the code it runs is 32 bits code? I think QEMU might be broken in that case. Try it in Bochs.
Re: problem when switching from protected mode to real mode
Posted: Fri Oct 24, 2008 1:59 am
by Combuster
1) you first need to be in 16-bit mode in order to disable the PE bit. Real computers don't like it, QEMU apparently continues in 32-bit realmode(!), I'm not sure what Bochs would do with this completely illegal move.
2) by loading NULL selectors you are screwing over the hidden portion of descriptor caches. The manuals require that you load realmode compatible values so that the descriptor caches have the right settings. Who knows what hardware does when you leave a non-readable non-writable cache setting in a segment register when PE is disabled and access to those fields gets locked down.
Re: problem when switching from protected mode to real mode
Posted: Fri Oct 24, 2008 5:09 am
by CodeCat
1) I've read about that before but I never quite figured out how to go into 16 bit protected mode. Does it involve jumping to a code segment with the size bit cleared? If so, I think
http://wiki.osdev.org/Real_mode needs fixing, cause it only loads 16 bit data selectors.
2) Is the 'real mode compatible' stuff the same trick that makes Unreal mode work?
Re: problem when switching from protected mode to real mode
Posted: Sat Oct 25, 2008 6:42 am
by Combuster
Yea, the protected mode article is broken. I googled and found a correct description of the procedure over at
osdever.net:
How do I get back to Real Mode?
On the '386:
* disable interrupts,
* do a far jump to a 16-bit code segment (i.e. switch briefly to 16-bit pmode),
* load SS with a selector to a 16-bit data/stack segment,
* clear the PE bit,
* do a far jump to a real-mode address,
* load the DS, ES, FS, GS, and SS registers with real-mode values,
* (optional)set IDTR to real-mode values (base 0, limit 0xFFFF),
* re-enable interrupts.
Edit: fixed wiki page. Amusingly, the asm code turned out to follow the correct sequence of actions anyway.
Re: problem when switching from protected mode to real mode
Posted: Sat Oct 25, 2008 12:22 pm
by osmosys
thanks friends...got it working by jumping into a segment with 16-bit operands..but now one more problem..interrupts don't work properly..i loaded real mode idt as said in the wiki page, i can see that a call is made to the correct address of real mode interrupts (saw in qemu log)..but no visible output occurs for int 10,int 16 and int 19...i am trying to chainload another os by loading its vbr and passing the execution control to it..its vbr is getting executed, interrputs are made to jump to the correct addresses but no visible output occurs..what could be the problem
here is the code
Code: Select all
cli
mov $0x30,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
jmp $0x28,$on_the_way_to_real_mode
hlt
.code16
on_the_way_to_real_mode:
movl %cr0,%eax
andl $0xfffffffe,%eax
movl %eax,%cr0
ljmp $0,$realmode
realmode:
xor %ax,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
mov %ax,%ss
mov $0x8000,%sp
lidt rmidt
sti
call 0x7C00
Code: Select all
and here is my idt
rmidt:
.2byte 0x3FF /* limit of the idt */
.4byte 0x0 /* base address of idt */
and this was my gdt
Code: Select all
gdtstructure:
.2byte 0x37 /* limit of the gdt */
.4byte gdtstart /* base address of gdt */
gdtstart:
/* null descriptor to ensure that no segment registers get loaded with value 0 */
/* causes "GENERAL PROTECTION FAULT" if we try to access this gdt entry */
.4byte 0
.4byte 0
/* code segment for our bootloader */
.2byte 0xFFFF, 0
.byte 0, 0x9A, 0xCF, 0
/* data segment for our bootloader */
.2byte 0xFFFF, 0
.byte 0, 0x92, 0xCF, 0
/* code segment for our kernel */
.2byte 0xFFFF, 0
.byte 0x10, 0x9A, 0xCF, 0
/* data segment for our kernel */
.2byte 0xFFFF, 0x0000
.byte 0x10, 0x92, 0xCF, 0
/* pseudo code segment for our chainloader */
.2byte 0xFFFF, 0
.byte 0x0, 0x9E, 0x0, 0
/* psuedo data segment for our chainloader */
.2byte 0xFFFF, 0x0000
.byte 0x0, 0x92, 0x0, 0
Re: problem when switching from protected mode to real mode
Posted: Sun Oct 26, 2008 6:49 am
by osmosys
got it solved..problem was i didn't load the stack segment with a 16-bit data segment and did not give any "good 16-bit value" to sp..