On 10 November 2017 at 18:11, Laszlo Ersek <[email protected]> wrote:
> On 11/10/17 16:56, Ard Biesheuvel wrote:
>> On 10 November 2017 at 15:49, Laszlo Ersek <[email protected]> wrote:
>>> This allows the PEI core to report the maximum temporary SEC/PEI stack
>>> usage on the DEBUG_INFO level, in the PeiCheckAndSwitchStack() function
>>> [MdeModulePkg/Core/Pei/Dispatcher/Dispatcher.c]:
>>>
>>> * Normal boot:
>>>
>>>> Temp Stack : BaseAddress=0x814000 Length=0x4000
>>>> Temp Heap  : BaseAddress=0x810000 Length=0x4000
>>>> Total temporary memory:    32768 bytes.
>>>>   temporary memory stack ever used:       3664 bytes. <----
>>>>   temporary memory heap used for HobList: 5904 bytes.
>>>>   temporary memory heap occupied by memory pages: 0 bytes.
>>>
>>> * S3 resume (with PEI decompression / SMM):
>>>
>>>> Temp Stack : BaseAddress=0x814000 Length=0x4000
>>>> Temp Heap  : BaseAddress=0x810000 Length=0x4000
>>>> Total temporary memory:    32768 bytes.
>>>>   temporary memory stack ever used:       3428 bytes. <----
>>>>   temporary memory heap used for HobList: 4816 bytes.
>>>>   temporary memory heap occupied by memory pages: 0 bytes.
>>>
>>> I unit-tested this change by transitorily adding an infinite loop right
>>> after the "rep stosd", and dumping the guest's temp SEC/PEI RAM (32KB
>>> currently) while the guest was stuck in the loop. The dump includes one
>>> dword from before and after the temp SEC/PEI RAM:
>>>
>>>> $ virsh qemu-monitor-command GUEST_NAME --hmp 'xp /8194wx 0x80FFFC'
>>>>
>>>> 000000000080fffc: 0x00000000 0x5aa55aa5 0x5aa55aa5 0x5aa55aa5
>>>> 000000000081000c: 0x5aa55aa5 0x5aa55aa5 0x5aa55aa5 0x5aa55aa5
>>>> ...
>>>> 0000000000817fec: 0x5aa55aa5 0x5aa55aa5 0x5aa55aa5 0x5aa55aa5
>>>> 0000000000817ffc: 0x5aa55aa5 0x00000000
>>>
>>> Cc: Ard Biesheuvel <[email protected]>
>>> Cc: Jordan Justen <[email protected]>
>>> Cc: Ruiyu Ni <[email protected]>
>>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=747
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Laszlo Ersek <[email protected]>
>>> ---
>>>  OvmfPkg/Sec/SecMain.inf        |  1 +
>>>  OvmfPkg/Sec/Ia32/SecEntry.nasm | 13 +++++++++++++
>>>  2 files changed, 14 insertions(+)
>>>
>>> diff --git a/OvmfPkg/Sec/SecMain.inf b/OvmfPkg/Sec/SecMain.inf
>>> index 711b59530907..6051cb3c6c4c 100644
>>> --- a/OvmfPkg/Sec/SecMain.inf
>>> +++ b/OvmfPkg/Sec/SecMain.inf
>>> @@ -71,6 +71,7 @@ [Pcd]
>>>    gEfiMdePkgTokenSpaceGuid.PcdGuidedExtractHandlerTableAddress
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdGuidedExtractHandlerTableSize
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdOvmfDecompressionScratchEnd
>>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdInitValueInTempStack
>>>
>>>  [FeaturePcd]
>>>    gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
>>> diff --git a/OvmfPkg/Sec/Ia32/SecEntry.nasm b/OvmfPkg/Sec/Ia32/SecEntry.nasm
>>> index 54d074e621f6..1d426fafa888 100644
>>> --- a/OvmfPkg/Sec/Ia32/SecEntry.nasm
>>> +++ b/OvmfPkg/Sec/Ia32/SecEntry.nasm
>>> @@ -29,6 +29,7 @@ extern ASM_PFX(SecCoreStartupWithStack)
>>>  ; @param[in]  EAX   Initial value of the EAX register (BIST: Built-in Self 
>>> Test)
>>>  ; @param[in]  DI    'BP': boot-strap processor, or 'AP': application 
>>> processor
>>>  ; @param[in]  EBP   Pointer to the start of the Boot Firmware Volume
>>> +; @param[in]  ES    Set to LINEAR_SEL in TransitionFromReal16To32BitFlat
>>
>> What does this mean? Does it belong in this patch? (Knowing you, and
>> noticing that the next patch adds it to the x86 version of this code
>> as well, I am sure it probably does, but I just need you to explain it
>> to me :-))
>
> See the STOSD instruction in the Intel SDM, or just on the web:
>
> http://x86.renejeschke.de/html/file_module_x86_id_306.html
>
>> Stores a byte, word, or doubleword from the AL, AX, or EAX register,
>> respectively, into the destination operand. The destination operand is
>> a memory location, the address of which is read from either the ES:EDI
>> or the ES:DI registers (depending on the address-size attribute of the
>> instruction, 32 or 16, respectively). The ES segment cannot be
>> overridden with a segment override prefix.
>
> It means that whatever we put in EDI, it will be relative to the segment
> "identified by" ES. (See the code comment near "mov edi": "base address,
> relative to ES".)
>
> Above I put "identified by" in quotes, because the definition of
> "identified by" depends on the mode the processor is in.
>
> (Instead of a botched attempt at writing up x86 segmentation (plus,
> optionally, paging) generally :) , I'll just leave these references
> here:
>
> * Intel SDM: 2.2 MODES OF OPERATION; Figure 2-3. Transitions Among the
>   Processor's Operating Modes
>
> * https://en.wikipedia.org/wiki/Unreal_mode
>
> * https://en.wikipedia.org/wiki/X86_memory_segmentation#80286_protected_mode
>
> * https://en.wikipedia.org/wiki/X86_memory_segmentation#80386_protected_mode
> )
>
> So using STOSD and ES:EDI as an example, here's a hugely inaccurate list
> of possible address calculations, dependent on processor mode:
>
> * real mode:
>
>   physical_address = ES * 0x10 + DI
>
>   E.g., A000:1234 means physical address A1234.
>
> * big real mode (segmentation enabled, paging disabled):
>
>   physical_address = GDT[ES].base_addr + EDI;
>
>   Basically, the value in ES is a "selector"; an offset into the GDT
>   (Global Descriptor Table). The base address of the GDT has been loaded
>   into the GDTR with the LGDT instruction. Then, at the offset
>   pointed-to by ES into the GDT, we find a segment descriptor entry,
>   which provides the base address of the segment (and some other
>   characteristics). The physical address is relative to that base.
>
> * 80386 protected mode (segmentation and paging):
>
>   virtual_address = GDT[ES].base_addr + EDI;
>   physical_address = paging(virtual_address);
>
>   Where in paging(), the CPU can use a cached translation from the TLB,
>   or perform a page table walk.
>
> In the code at hand, we are in "big real mode" (also called unreal mode,
> 32-bit flat mode). Segmentation is enabled, paging is not. The segment
> selector in ES is an "implicit" parameter for the code being added,
> because the STOSD instruction relies on ES.
>
> We move from real mode to big real mode earlier in
>
>   UefiCpuPkg/ResetVector/Vtf0/Ia16/Real16ToFlat32.asm
>
>> %define SEC_DEFAULT_CR0  0x40000023                               <---- bit 
>> 31, "Paging Enabled", is clear
>>                                                                         bit  
>> 0, "Protection Enable", is set, it will enable segmentation
>> %define SEC_DEFAULT_CR4  0x640
>>
>> BITS    16
>>
>> ;
>> ; Modified:  EAX, EBX
>> ;
>> TransitionFromReal16To32BitFlat:
>>
>>     debugShowPostCode POSTCODE_16BIT_MODE
>>
>>     cli
>>
>>     mov     bx, 0xf000
>>     mov     ds, bx
>>
>>     mov     bx, ADDR16_OF(gdtr)
>>
>> o32 lgdt    [cs:bx]                                               <---- 
>> loads the GDT's address into GDTR
>>
>>     mov     eax, SEC_DEFAULT_CR0
>>     mov     cr0, eax
>>
>>     jmp     LINEAR_CODE_SEL:dword ADDR_OF(jumpTo32BitAndLandHere) <--- 
>> actual mode switch
>> BITS    32
>> jumpTo32BitAndLandHere:
>>
>>     mov     eax, SEC_DEFAULT_CR4
>>     mov     cr4, eax
>>
>>     debugShowPostCode POSTCODE_32BIT_MODE
>>
>>     mov     ax, LINEAR_SEL
>>     mov     ds, ax
>>     mov     es, ax                                                <---- this 
>> is where ES gets the LINEAR_SEL offset into the GDT
>>     mov     fs, ax
>>     mov     gs, ax
>>     mov     ss, ax
>>
>>     OneTimeCallRet TransitionFromReal16To32BitFlat
>
> The LINEAR_SEL offset is defined lower down in the same file, where the
> actual contents of that segment descriptor are defined as well:
>
>> ;
>> ; The Global Descriptor Table (GDT)
>> ;
>>
>> GDT_BASE:
>> ; null descriptor
>> NULL_SEL            equ $-GDT_BASE
>>     DW      0            ; limit 15:0
>>     DW      0            ; base 15:0
>>     DB      0            ; base 23:16
>>     DB      0            ; sys flag, dpl, type
>>     DB      0            ; limit 19:16, flags
>>     DB      0            ; base 31:24
>>
>> ; linear data segment descriptor
>> LINEAR_SEL          equ $-GDT_BASE
>>     DW      0xffff       ; limit 15:0
>>     DW      0            ; base 15:0
>>     DB      0            ; base 23:16
>>     DB      PRESENT_FLAG(1)|DPL(0)|SYSTEM_FLAG(1)|DESC_TYPE(DATA32_TYPE)
>>     DB      
>> GRANULARITY_FLAG(1)|DEFAULT_SIZE32(1)|CODE64_FLAG(0)|UPPER_LIMIT(0xf)
>>     DB      0            ; base 31:24
>>
>> [...]
>
> The point is that this descriptor defines a segment that is based at
> address 0, and limited at address 4GB. (Because "limit", including
> UPPER_LIMIT(0xf), gives 0xf_ffff, and GRANULARITY_FLAG(1) means the
> limit is expressed in 4KB pages).
>
> So, I added the "@param[in]  ES" comment to show that I was aware of:
>
> - being in unreal mode (in fact the file being patched itself states
>   "Processor is in flat protected mode"),
>
> - STOSD using ES,
>
> - ES having the selector value LINEAR_SEL,
>
> - the segment descriptor pointed-to by LINEAR_SEL defining a 0-based
>   32-bit segment, meant for "read/write data".
>
> In other words, that I was allowed to use EDI as a plain, zero-based
> physical address.
>

Excellent clarification, thanks.
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to