On 12/05/13 00:21, Yao, Jiewen wrote: > Hi Laszlo > Thanks for the investigation. > > Maybe I am wrong, but I would like to point out this piece of code works fine > on IA real platform.
Thanks for confirming that! I was wondering if it was in active use. > We also have other piece of code to jump from 64bit to 16bit directly, which > works fine also. > > I just checked IA32 manual, for far return, detail below: (error handling are > removed) Yes I've been staring at the far return documentation for days now :) > Would you please help to check if the emulator follow IA32 manual to do the > mode switch? I've already asked on an internal list and got a discussion started, but I think I'll have to keep asking. The interesting thing is that this lret instruction doesn't seem to trap to KVM at all (although KVM does have a function to emulate this, em_ret_far()). I could have messed up the tracing of course. Right now it seems to me that the reboot happens internally to the guest, without a VMEXIT or so. (Not sure if what I'm saying makes any sense, sorry...) > =================================== > (* IA-32e Mode *) > IF (PE = 1 and VM = 0 and IA32_EFER.LMA = 1) and instruction = far return > GOTO IA-32E-MODE-RETURN-SAME-PRIVILEGE-LEVEL > > IA-32E-MODE-RETURN-SAME-PRIVILEGE-LEVEL: > IF OperandSize = 32 > THEN > EIP <- Pop(); > CS <- Pop(); (* 32-bit pop, high-order 16 bits discarded *) > ELSE > IF OperandSize = 16 > THEN > EIP <- Pop(); > EIP <- EIP AND 0000FFFFH; > CS <- Pop(); (* 16-bit pop *) > ELSE (* OperandSize = 64 *) > RIP <- Pop(); > CS <- Pop(); (* 64-bit pop, high-order 48 bits discarded *) > FI; > FI; > IF instruction has immediate operand > THEN (* Release parameters from stack *) > IF StackAddressSize = 32 > THEN > ESP <- ESP + SRC; > ELSE > IF StackAddressSize = 16 > THEN > SP <- SP + SRC; > ELSE (* StackAddressSize = 64 *) > RSP <- RSP + SRC; > FI; > FI; > FI; > =================================== I sought to check the error handling too, in the SDM and in the KVM source, and I couldn't find anything that we'd hit. Can you please help me understand the routine though? >>>> ASM_GLOBAL ASM_PFX(AsmTransferControl) >>>> ASM_PFX(AsmTransferControl): >>>> # rcx S3WakingVector :DWORD >>>> # rdx AcpiLowMemoryBase :DWORD >>>> lea _AsmTransferControl_al_0000(%rip), %eax >>>> movq $0x2800000000, %r8 >>>> orq %r8, %rax >>>> pushq %rax >>>> shrd $20, %ecx, %ebx >>>> andl $0x0f, %ecx >>>> movw %cx, %bx >>>> movl %ebx, jmp_addr(%rip) >>>> lret <-------------- here So the intent of the lret is mostly clear to me. What are we doing with ecx and edx? On input rcx holds the S3WakingVector parameter, which is 0x9A1D0 in my case. Any real mode vector will (I guess) occupy at most 20 bits (< 1MB). The shrd seems to shift those bits from the least significant end of ecx into the most significant end of ebx, right? ecx: 00000000 00001001 10100001 11010000 == 0x0009A1D0 ebx: 10011010 00011101 0000???? ???????? == 0x9A1D0??? Then the andl masks out everything in ecx except the least significant nibble: ecx: 00000000 00000000 00000000 00000000 == 0x00000000 ebx: 10011010 00011101 0000???? ???????? == 0x9A1D0??? We copy the least significant 16 bits from ecx to ebx: ecx: 00000000 00000000 00000000 00000000 == 0x00000000 ebx: 10011010 00011101 00000000 00000000 == 0x9A1D0000 and we write ebx to the .long at jmp_addr. >>>> _AsmTransferControl_al_0000: >>>> .byte 0x0b8, 0x30, 0 # mov ax, 30h as selector Why is it necessary to express this in raw binary? We could simply say "movl $0, %eax", couldn't we? >>>> movl %eax, %ds >>>> movl %eax, %es >>>> movl %eax, %fs >>>> movl %eax, %gs >>>> movl %eax, %ss >>>> movq %cr0, %rax OK so this surprises me (but as you can tell my assembly is quite weak). The segment descriptor we selected with CS=0x28 at the top tells the CPU that this code here should be executed in compatibility mode. According to the SDM, "MOV—Move to/from Control Registers", MOV r64, CR0–CR7 # Move extended control register to r64. is N.E. in compatibility/legacy mode. Are we not in compat mode? If the processor is still in 64-bit mode, what is the CS change good for? >>>> movq %cr4, %rbx >>>> .byte 0x66 >>>> andl $0x7ffffffe, %eax I guess the 0x66 prefix is justified because our CS change resulted in the default address & operand width being 16 bits, and we need 32 bit operands here. We mask off the MSB and LSB of eax, which seem to correspond to Paging and Protection Enable, respectively. (This would enable real mode when written back to cr0.) >>>> andb $0xdf, %bl Turns off bit 5 in cr4's value, which is Physical Address Extension (PAE). >>>> movq %rax, %cr0 At this point we seem to reenter real mode (turning off Paging and Protection Enable). >>>> .byte 0x66 >>>> movl $0x0c0000080, %ecx Is a reference to "ecx" still valid in real mode? >>>> rdmsr >>>> andb $0xfe, %ah >>>> wrmsr This seems to clear bit 8 in IA32_EFER, which stands for "IA-32e Mode Enable". >>>> movq %rbx, %cr4 Turns off PAE. We're surely not in 64-bit mode here though? And MOV CR0–CR7, r64 # Move r64 to extended control register. is N.E. (not encodable) in compat/legacy mode. >>>> .byte 0x0ea # jmp far jmp_addr >>>> jmp_addr: >>>> .long 0 EA cd JMP ptr16:16 Jump far, absolute, address given in operand The operand is 0x9A1D0000, meaning 9A1D:0, which seems to correspond to the original 0x9A1D0 input param. And looking back at the ebx calculation at the top, a nonzero offset (<= 0xf) would be stored correctly as well. Thank you, Laszlo ------------------------------------------------------------------------------ Sponsored by Intel(R) XDK Develop, test and display web and hybrid apps with a single code base. Download it for free now! http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel