problem when switching from protected mode to real mode

Programming, for all ages and all languages.
Post Reply
osmosys
Posts: 14
Joined: Sun Oct 05, 2008 4:33 am

problem when switching from protected mode to real mode

Post 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
CodeCat
Member
Member
Posts: 158
Joined: Tue Sep 23, 2008 1:45 pm
Location: Eindhoven, Netherlands

Re: problem when switching from protected mode to real mode

Post 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.
osmosys
Posts: 14
Joined: Sun Oct 05, 2008 4:33 am

Re: problem when switching from protected mode to real mode

Post 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) ?
CodeCat
Member
Member
Posts: 158
Joined: Tue Sep 23, 2008 1:45 pm
Location: Eindhoven, Netherlands

Re: problem when switching from protected mode to real mode

Post 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?
osmosys
Posts: 14
Joined: Sun Oct 05, 2008 4:33 am

Re: problem when switching from protected mode to real mode

Post 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...
CodeCat
Member
Member
Posts: 158
Joined: Tue Sep 23, 2008 1:45 pm
Location: Eindhoven, Netherlands

Re: problem when switching from protected mode to real mode

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: problem when switching from protected mode to real mode

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
CodeCat
Member
Member
Posts: 158
Joined: Tue Sep 23, 2008 1:45 pm
Location: Eindhoven, Netherlands

Re: problem when switching from protected mode to real mode

Post 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?
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: problem when switching from protected mode to real mode

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
osmosys
Posts: 14
Joined: Sun Oct 05, 2008 4:33 am

Re: problem when switching from protected mode to real mode

Post 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
osmosys
Posts: 14
Joined: Sun Oct 05, 2008 4:33 am

Re: problem when switching from protected mode to real mode

Post 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..
Post Reply