hi, all, thank you for your attention.
I also write ASM code sequence to switch ia32e (64-bit mode)-> ia32e(compatibility mode) ->protected mode with paging. But it fails at the second stage (compatibility mode -> protected mode). The sequence is like:
1. Create x86_32 page table
2. Switch to compatibility mode
3. De-activate IA-32e mode by clearing CR0.PG,and disable PAE
4. Load cr3 with x86_32 page table
5. Disable IA-32e mode by setting IA32_EFER.LME
6. Enable legacy paging protected mode by setting CR0.PG=1
7. Switch GDTR, cs,ds,es,fs,gs,ss to x86_32 mode (failed.)
8. Jump to execute 1st instruction in protected mode.
p.s. this is source code, it is built on RHEL5U2 x64. its objdump code is after it. I don't know why lgdt failed when it tries to load GDT for protected mode.
Code: Select all
/*
void nuc_switch_to_protected_mode(unsigned long rip,unsigned long rsi)
*/
//rdi,rsi,rdx,rcx,r8,r9
.global nuc_switch_to_protected_mode
nuc_switch_to_protected_mode:
mov %rdi,%rbx /*rbx, target eip*/
/*create 32-bit page table: 1G 1:1 mapping, this switch code is also on the 1:1 mapping.*/
movl $(sym_phys(guest_pg)),%edi
movl $(sym_phys(legacy_pg_table)),%edx
movl $0x7,%eax
movl $0,%ebp
10:
leal 0x7(%edi),%ecx /*create PDE entry*/
movl %ecx,(%edx) /*store PDE entry*/
addl $4,%edx /*increase PDE index*/
addl $1,%ebp
movl $1024,%ecx
11:
stosl
addl $0x1000,%eax
loop 11b
cmpl $256,%ebp
jne 10b
/*1.switch to compatibility mode*/
pushq $(__HYPERVISOR_CS32)
leaq enter_compability_mode(%rip),%rax
leaq enter_protected_mode(%rip),%rbp
pushq %rax
lretq
.code32
enter_compability_mode:
/*In compatibility now*/
/*2.deactive IA-32e mode by clear CR0.PG*/
mov %cr0,%eax
and $0x7fffffff,%eax
mov %eax,%cr0
/*disable PAE*/
mov %cr4,%eax
and $0xffffffdf,%eax /*clear PAE,bit 5*/
mov %eax,%cr4
/*3.load cr3 with legency page table*/
mov $(sym_phys(legacy_pg_table)),%eax
mov %eax,%cr3
/*4.disable IA-32e mode by setting IA32_EFER.LME*/
mov $MSR_EFER,%ecx
rdmsr
btrl $_EFER_LME,%eax /*clear LME*/
wrmsr
/*5.enable legacy paged-protected mode by setting CR0.PG=1*/
mov %cr0,%eax
orl $0x80000000,%eax
mov %eax,%cr0
/*setup guest's segmentation in protected mode*/
/*BUG: Here*/
lgdt sym_phys(guest_boot_gdt_descr) //it fails here.
movl $GUEST_BOOT_DS,%eax
movl %eax,%gs
movl %eax,%ss
movl %eax,%fs
movl %eax,%ds
movl %eax,%es
push $GUEST_BOOT_CS
push %ebp /*High 32-bit is truncted.*/
iret
enter_protected_mode:
/*6.branch to instructions in protected mode*/
jmp *%ebx
ret
the dumping code is:
Code: Select all
ffffc10028119084 <switch_to_protected_mode>:
ffffc10028119084: 48 89 fb mov %rdi,%rbx
ffffc10028119087: bf 00 90 00 28 mov $0x28009000,%edi
ffffc1002811908c: ba 00 80 00 28 mov $0x28008000,%edx
ffffc10028119091: b8 07 00 00 00 mov $0x7,%eax
ffffc10028119096: bd 00 00 00 00 mov $0x0,%ebp
ffffc1002811909b: 67 8d 4f 07 addr32 lea 0x7(%edi),%ecx
ffffc1002811909f: 67 89 0a addr32 mov %ecx,(%edx)
ffffc100281190a2: 83 c2 04 add $0x4,%edx
ffffc100281190a5: 83 c5 01 add $0x1,%ebp
ffffc100281190a8: b9 00 04 00 00 mov $0x400,%ecx
ffffc100281190ad: ab stos %eax,%es:(%rdi)
ffffc100281190ae: 05 00 10 00 00 add $0x1000,%eax
ffffc100281190b3: e2 f8 loop ffffc100281190ad <switch_to_protected_mode+0x29>
ffffc100281190b5: 81 fd 00 01 00 00 cmp $0x100,%ebp
ffffc100281190bb: 75 de jne ffffc1002811909b <switch_to_protected_mode+0x17>
ffffc100281190bd: 68 38 e0 00 00 pushq $0xe038
ffffc100281190c2: 48 8d 05 0a 00 00 00 lea 10(%rip),%rax # ffffc100281190d3 <enter_compability_mode>
ffffc100281190c9: 48 8d 2d 51 00 00 00 lea 81(%rip),%rbp # ffffc10028119121 <enter_protected_mode>
ffffc100281190d0: 50 push %rax
ffffc100281190d1: 48 cb lretq
ffffc100281190d3 <enter_compability_mode>:
ffffc100281190d3: 0f 20 c0 mov %cr0,%rax
ffffc100281190d6: 25 ff ff ff 7f and $0x7fffffff,%eax
ffffc100281190db: 0f 22 c0 mov %rax,%cr0
ffffc100281190de: 0f 20 e0 mov %cr4,%rax
ffffc100281190e1: 83 e0 df and $0xffffffffffffffdf,%eax
ffffc100281190e4: 0f 22 e0 mov %rax,%cr4
ffffc100281190e7: b8 00 80 00 28 mov $0x28008000,%eax
ffffc100281190ec: 0f 22 d8 mov %rax,%cr3
ffffc100281190ef: b9 80 00 00 c0 mov $0xc0000080,%ecx
ffffc100281190f4: 0f 32 rdmsr
ffffc100281190f6: 0f ba f0 08 btr $0x8,%eax
ffffc100281190fa: 0f 30 wrmsr
ffffc100281190fc: 0f 20 c0 mov %cr0,%rax
ffffc100281190ff: 0d 00 00 00 80 or $0x80000000,%eax
ffffc10028119104: 0f 22 c0 mov %rax,%cr0
ffffc10028119107: 0f 01 15 02 90 10 28 lgdt 672174082(%rip) # ffffc10050222110 <_end+0x27f9d800> ///failed here.
ffffc1002811910e: b8 18 00 00 00 mov $0x18,%eax
ffffc10028119113: 8e e8 movl %eax,%gs
ffffc10028119115: 8e d0 movl %eax,%ss
ffffc10028119117: 8e e0 movl %eax,%fs
ffffc10028119119: 8e d8 movl %eax,%ds
ffffc1002811911b: 8e c0 movl %eax,%es
ffffc1002811911d: 6a 10 pushq $0x10
ffffc1002811911f: 55 push %rbp
ffffc10028119120: cf iret
Code: Select all
ENTRY(legacy_pg_table)
.fill 1024,4,0
/*1:1 mapping in 1G*/
#define GUEST_MAP_END 1024*1024*1024
ENTRY(guest_pg)
.fill 1024*256,4,0
#define GUEST_GDT_ENTRY_BOOT_CS 2
#define GUEST_BOOT_CS (GUEST_GDT_ENTRY_BOOT_CS*8)
#define GUEST_GDT_ENTRY_BOOT_DS (GUEST_GDT_ENTRY_BOOT_CS + 1)
#define GUEST_BOOT_DS (GUEST_GDT_ENTRY_BOOT_DS*8)
# early boot GDT descriptor (must use 1:1 address mapping)
.word 0 # 32 bit align gdt_desc.address
guest_boot_gdt_descr:
.word 16*4096
.long sym_phys(guest_boot_gdt_table)
/*
* The boot_gdt_table must mirror the equivalent in setup.S and is
* used only for booting.
*/
.align 128
ENTRY(guest_boot_gdt_table)
.quad 0x0000000000000000 /* 0x0 unused */
.quad 0x0000000000000000 /* 0x8 unused */
.quad 0x00cf9a000000ffff /* 0x10 GUEST_BOOT_CS: kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 GUEST_BOOT_DS: kernel 4GB data at 0x00000000 */
.fill FIRST_RESERVED_GDT_ENTRY-4,8,0
.quad 0x0000000000000000 /* unused */
.quad 0x00af9a000000ffff /* 0xe008 ring 0 code, 64-bit mode */
.quad 0x00cf92000000ffff /* 0xe010 ring 0 data */
.quad 0x0000000000000000 /* reserved */
.quad 0x00cffa000000ffff /* 0xe023 ring 3 code, compatibility */
.quad 0x00cff2000000ffff /* 0xe02b ring 3 data */
.quad 0x00affa000000ffff /* 0xe033 ring 3 code, 64-bit mode */
.quad 0x00cf9a000000ffff /* (HYPERVISOR_CS32)0xe038 ring 0 code, compatibility */