Re: Could not switch back to Real-Address mode from Protected Mode. Help?



Jean-François Michaud wrote:
jfguo wrote:
I am writing a function to switch from Protected Mode to Real Mode, but
the code does't work. The program crashes when doing the second far
jump.
I followed the steps of section 9.9.2 of Intel IA32 Manual Vol3 (June
2005 version),
and checked the relevant threads in this forum. But still can't figure
out where is my error. The following is my codes.

Any helps, thanks a lot.
jfguo

/* Segment Descriptor table. */
SEGMENT_DESCRIPTOR RealGdt[] = {
{0, 0, 0, 0, 0, 0}, //First Descriptor in GDT is not used
{0xffff, 0x0000, 0x09, 0x9b, 0x00, 0x00}, //Protected/Real 16bit
code, 0x08
{0xffff, 0x0000, 0x00, 0x9f, 0xcf, 0x00} //protected flat mode
code, 0x10
};

;At this point, the CPU operates at Protected mode with flat memory
model.
;

;Step 1. Disable interrupts.
cli

;Step 2 of IA32 Manual. If paging is enabled, ...
;Because the paging is not enabled, I omited this step.

lgdt fword ptr [RealGdtr]

;Step 3 of IA32 Manual.
;Because I loaded my code at 90000h, so I subtract 90000h from the
PMode16bit.
DB 0EAh
DD PMode16bit-90000h
DW 08h
PMode16bit:

;92h is outputed at port 80h correct.
mov al, 92h
out 80h, al

;Step 4 of IA32 Manual. Load segment registers.
;Omit. I want using the descriptor attributes loaded during protected
mode.

;Step 5 of IA32 Manual. Execute LIDT.
;Omit.

;Step 6 of IA32 Manual. Clear the PE flag.
mov eax, cr0
and al, 0feh
mov cr0, eax

;Step7 of IA32 Manual.
;Far jump to the real-address mode code.
DB 66h
DB 0EAh
DD RealMode16bit-90000h
DW 08h
RealMode16bit:
;93h is not outputed in port 80.
mov al, 93h
out 80h, al


Here is my bit of code for doing it (AT&T syntax):

jmp $0x20, $mode16 # jump to appropriate descriptor

.code16

mode16:

movw $0x18, %ax # We point to the data selector
movw %ax, %ds
movw %ax, %es
movw %ax, %gs
movw %ax, %fs
movw %ax, %ss
movw $0x7C00, %sp

lidt ivtr

movl %cr0, %eax # We recuperate the content of control register
0
and $0xFE, %al # We set the PE bit to 1
movl %eax, %cr0 # We write it back down in the control register
0

jmp $0x0, $realmode


This is my gtd and ivtr:

# This is the GDTR
gdtr:
.word gdt_end - gdt - 1 # Limit of the GDT
.long gdt # Base of the GDT

# This is the GDT
gdt:
.long 0x00000000 # Null descriptor
.long 0x00000000 #
gdt_32data:
.long 0x0000FFFF # Data segment descriptor
.long 0x00CF9200 # read/write
gdt_32code:
.long 0x0000FFFF # Code segment descriptor
.long 0x00CF9800 # execute/read
gdt_16data:
.long 0x0000FFFF
.long 0x00009200 # read/write
gdt_16code:
.long 0x0000FFFF
.long 0x00009800 # execute/read
gdt_end:

# This is the IDTR
ivtr:
.word 0x03FF # Limit of the real mode IVT
.long 0x0 # Base of the real mode IVT

If you wrote over the real mode vector table then then you can't use
interrupts in real mode so you shouldn't enable them. My guess is you
my want to use them so make sure you don't overwrite the data in the
range 0x0-0x03FF while in protected mode.

Also, if you remap the interrupts in protected mode, you have to remap
them to their original values before going back to real mode. Le me
know if you need to do that.

Regards
Jean-Francois Michaud

Thanks for the reply.
I have tried your method. It works.
Indeed I want to use interrupt in real mode. So I put my
code at address 0x90000 in case of conflicting with the
real mode IVT.

I still have some questions.
Is it necessary that these mode switching codes must resides
at address 0x0? And If it is necessary, how can I avoid overwriting
the range 0x0-0x3FF? Use the directive ORG?
Why the segment descriptor in the second far jump (jmp $0x0, $realmode)
of your code is 0x0? The 0x0 is a null descriptor. I have tried to
change it to other values, but it failed.

Best Regards
Jianfeng Guo

.



Relevant Pages