On 2015-10-27 05:20:12, Laszlo Ersek wrote:
> On 10/27/15 07:48, Jordan Justen wrote:
> > What about adding this code to mStartupCodeTemplate and sending the
> > APs there using the MP services?
> 
> That was the idea in Jeff's earlier series, but it comes with a number
> of complications. The main one is that the HLT loop is entered as a
> "normal" StartupAllAPs() callback function. That necessitates tearing
> down parts of the environment that the MP services implementation builds
> up carefully at initialization. Other parts of that laborously
> constructed environment are plain irrelevant.

Hmm. I assumed that starting an AP task should be much lower latency
than running INIT-SIPI-SIPI. Yet, examining the current code, it looks
like we generally call CpuSleep on the AP, and we only currently
support resuming from this state with an INIT-SIPI-SIPI.

I think the APs could be woken from CPU sleep by an interrupt IPI,
which should be a lot less traumatic than INIT-SIPI-SIPI. This
probably would require a stub interrupt table to be setup for the APs.

With that in place, we could then send the APs to the cli-hlt loop
much faster than via INIT-SIPI-SIPI.

(Especially given that apparently this can take up to 1 second?! :)

Another reason it could be good to work with the code is that we
probably should put the code into a shutdown mode, so if future MP
service requests come in, the MP services code doesn't try to restart
the APs. (Ie, it should just return an immediate error.)

> Instead, this solution takes manual mode switching, paging, and stacks
> out of the picture, which is a good thing. The INIT-SIPI-SIPI sequence
> places the AP in real mode immediately, which is perfect for the minimal
> stub that we'd like the APs to run.

I didn't consider the potential problem with leaving paging enabled,
but if we get the processor back into IA32 mode, we can leave the APs
running anywhere in the low 32-bit address space. It would be nice to
not to leave any memory allocated below 1MB if we can avoid it.

> > Regardless of that, I think the C-based-asm would better fit in
> > ApStartup.c.
> 
> I can move the code of course, but to me it doesn't seem overly
> justified. The C-based ASM in this question is only used by the
> ExitBootServices() callback, so (as you can see it in the patch) I could
> keep the template STATIC. If I move it to ApStartup.c, I'll have to make
> it extern (or else do the copying outside of the callback). I feel that
> this isn't overly justified, but if you insist, I can do it.

Ok. This is fine. I am not a fan of the C-base-asm, despite the fact
that I wrote it, and I still think it is better than separate assembly
files. (Having only NASM be used in EDK II might change my mind.)

I guess I just don't want to see it proliferate too much. :)

> > Why not just a cli-hlt loop?
> 
> Because if the ExitBootServices() callback doesn't wait until all APs
> abort and abandon the MP services idle loop -- or worse, an assigned MP
> services job that is still running -- then the BSP could immediately
> return from ExitBootServices(), and proceed to third party code that:
> - overwrites the page that used to host the idle loop, or
> - raises an SMI (via a variable service call for example) and then the
>   APs might return to the old idle loop upon seeing RSM.
> 
> The synchronization is in place so that the callback returns knowing
> that all APs have abandoned the old idle loop (or whatever job they were
> running).

We could verify all APs have set CpuStateBusy. By that time it seems
extremely likely that all APs will be running in the runtime safe
code.

-Jordan

> 
> > Maybe if the MP services were used then
> > the separate counter goes away.
> 
> I don't want it to go away. :) Plus, as far as I can see in
> StartupAllAPs() [UefiCpuPkg/CpuDxe/CpuMp.c], it doesn't wait for the
> *initiation* of the user procedure in any case. It can wait for the
> completion, but that's not good to us now -- our procedure is an
> infinite loop; it will not complete. All we care about is feedback that
> the old code has been abandoned completely.
> 
> > How about 0 rather than 0x1234?
> 
> I wanted something that would reflect the little endian encoding in a
> pure hexdump. I don't feel strongly about this though.
> 
> > Can you provide a nasm source block in a comment like in ApStartup.c?
> 
> Sure.
> 
> Please follow up one by one on the points where I disagreed, so I can
> send v2. The two parts that I do feel strongly about are (a) not using
> MP services itself for this, (b) the counter / synchronization.
> 
> Thanks
> Laszl
> 
> > 
> > -Jordan
> > 
> > On 2015-10-26 14:33:09, Laszlo Ersek wrote:
> >> Our current callback puts the APs in wait-for-SIPI state, by sending them
> >> an INIT IPI. This works fine as long as the APs are not woken between
> >> ExitBootServices() and the time the runtime OS boots the APs.
> >>
> >> However, if a platform includes PiSmmCpuDxeSmm, and bases the runtime UEFI
> >> services (like the variable services) on SMM and SMIs, then two problem
> >> scenarios can be experienced:
> >>
> >> (a) On virtual platforms, the APs can be (spuriously?) woken by SMIs,
> >>     between ExitBootServices() and the runtime OS's AP boot; for example
> >>     by variable service calls. In this case the APs have no code to
> >>     continue executing after the RSM instruction, which causes them to
> >>     re-set.
> >>
> >> (b) On physical platforms, the APs might not wake from the wait-for-SIPI
> >>     state at all, causing performance (or functional) problems with
> >>     PiSmmCpuDxeSmm, dependent on the BSP/AP sync mode configured therein.
> >>
> >> Implement the following logic instead:
> >>
> >> - If there is no AP, there's no need for an ExitBootServices() callback.
> >>
> >> - Otherwise, reboot all APs with a minimal real-mode stub. Each AP shall
> >>   bump a shared counter atomically at first, then spiral into an infinite
> >>   HLT loop. This occurs regardless of what each AP has been doing at that
> >>   moment.
> >>
> >> - APs can wake from HLT due to an SMI easily.
> >>
> >> - The BSP in the ExitBootServices() callback will wait until all APs have
> >>   moved on to the stub.
> >>
> >> - Since we exit boot services time now with the original MP services
> >>   startup code abandoned by all APs, we can even allocate that startup
> >>   code in Boot Services Code type memory; only the new stub has to be
> >>   AcpiNVS.
> >>
> >> Cc: Chen Fan <[email protected]>
> >> Cc: Jeff Fan <[email protected]>
> >> Cc: Jordan Justen <[email protected]>
> >> Cc: Michael Kinney <[email protected]>
> >> Contributed-under: TianoCore Contribution Agreement 1.0
> >> Signed-off-by: Laszlo Ersek <[email protected]>
> >> ---
> >>
> >> Notes:
> >>     So this would be option (2) from
> >>     <http://thread.gmane.org/gmane.comp.bios.edk2.devel/3357/focus=3403>.
> >>     With this patch in place, the AP reboots I had seen on TCG are gone.
> >>
> >>  UefiCpuPkg/CpuDxe/ApStartup.c |  2 +-
> >>  UefiCpuPkg/CpuDxe/CpuMp.c     | 84 
> >> +++++++++++++++++++++++++++++++++++--------
> >>  2 files changed, 71 insertions(+), 15 deletions(-)
> >>
> >> diff --git a/UefiCpuPkg/CpuDxe/ApStartup.c b/UefiCpuPkg/CpuDxe/ApStartup.c
> >> index 78fb26f..b8d736f 100644
> >> --- a/UefiCpuPkg/CpuDxe/ApStartup.c
> >> +++ b/UefiCpuPkg/CpuDxe/ApStartup.c
> >> @@ -382,7 +382,7 @@ PrepareAPStartupCode (
> >>    StartAddress = BASE_1MB;
> >>    Status = gBS->AllocatePages (
> >>                    AllocateMaxAddress,
> >> -                  EfiACPIMemoryNVS,
> >> +                  EfiBootServicesCode,
> >>                    EFI_SIZE_TO_PAGES (sizeof (*StartupCode)),
> >>                    &StartAddress
> >>                    );
> >> diff --git a/UefiCpuPkg/CpuDxe/CpuMp.c b/UefiCpuPkg/CpuDxe/CpuMp.c
> >> index 04c2f1f..98976a1 100644
> >> --- a/UefiCpuPkg/CpuDxe/CpuMp.c
> >> +++ b/UefiCpuPkg/CpuDxe/CpuMp.c
> >> @@ -30,6 +30,33 @@ VOID *mApStackStart = 0;
> >>  volatile BOOLEAN mAPsAlreadyInitFinished = FALSE;
> >>  volatile BOOLEAN mStopCheckAllAPsStatus = TRUE;
> >>  
> >> +#pragma pack (1)
> >> +typedef struct {
> >> +  UINT8  MovAx;
> >> +  UINT16 AxValue;
> >> +  UINT8  MovDsAx[2];
> >> +  UINT8  LockedWordCounterIncrement[6];
> >> +
> >> +  UINT8  Cli;
> >> +  UINT8  Hlt;
> >> +  UINT8  JmpToCli[2];
> >> +
> >> +  UINT16 WordCounter;
> >> +} CPU_AP_HLT_LOOP;
> >> +#pragma pack ()
> >> +
> >> +STATIC CONST CPU_AP_HLT_LOOP mCpuApHltLoopTemplate = {
> >> +  0xB8, 0x1234,                           // mov ax,0x1234
> >> +  { 0x8E, 0xD8 },                         // mov ds,ax
> >> +  { 0xF0, 0x3E, 0xFF, 0x06, 0x0F, 0x00 }, // lock inc word [ds:0xf]
> >> +
> >> +  0xFA,                                   // cli @ offset 0xb
> >> +  0xF4,                                   // hlt
> >> +  { 0xEB, 0xFC },                         // jmp short 0xb
> >> +
> >> +  0x0000                                  // WordCounter @ offset 0xf
> >> +};
> >> +
> >>  EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
> >>    GetNumberOfProcessors,
> >>    GetProcessorInfo,
> >> @@ -1659,11 +1686,31 @@ ExitBootServicesCallback (
> >>    IN VOID                     *Context
> >>    )
> >>  {
> >> +  CPU_AP_HLT_LOOP *CpuApHltLoop;
> >> +  UINT16          ApCount;
> >> +
> >> +  CpuApHltLoop = Context;
> >> +  CopyMem (CpuApHltLoop, &mCpuApHltLoopTemplate, sizeof 
> >> mCpuApHltLoopTemplate);
> >> +
> >>    //
> >> -  // Avoid APs access invalid buff datas which allocated by BootServices,
> >> -  // so we send INIT IPI to APs to let them wait for SIPI state.
> >> +  // Point DS via AX to the page where the routine has been allocated.
> >>    //
> >> -  SendInitIpiAllExcludingSelf ();
> >> +  CpuApHltLoop->AxValue = (UINT16)((UINTN)Context >> 4);
> >> +  DEBUG ((EFI_D_VERBOSE, "%a: %a: HLT loop routine at %04x:%04x\n",
> >> +    gEfiCallerBaseName, __FUNCTION__, CpuApHltLoop->AxValue, 0));
> >> +
> >> +  //
> >> +  // Force all APs to execute the HLT loop. Wait until they all enter the 
> >> code.
> >> +  //
> >> +  SendInitSipiSipiAllExcludingSelf ((UINT32)(UINTN)Context);
> >> +
> >> +  ApCount = (UINT16)(mMpSystemData.NumberOfProcessors - 1);
> >> +  while (InterlockedCompareExchange16 (
> >> +           &CpuApHltLoop->WordCounter,
> >> +           ApCount,
> >> +           ApCount) != ApCount) {
> >> +    CpuPause();
> >> +  }
> >>  }
> >>  
> >>  /**
> >> @@ -1691,9 +1738,10 @@ InitializeMpSupport (
> >>    VOID
> >>    )
> >>  {
> >> -  EFI_STATUS     Status;
> >> -  MTRR_SETTINGS  MtrrSettings;
> >> -  UINTN          Timeout;
> >> +  EFI_STATUS           Status;
> >> +  MTRR_SETTINGS        MtrrSettings;
> >> +  UINTN                Timeout;
> >> +  EFI_PHYSICAL_ADDRESS CpuApHltLoopAddress;
> >>  
> >>    gMaxLogicalProcessorNumber = (UINTN) PcdGet32 
> >> (PcdCpuMaxLogicalProcessorNumber);
> >>    if (gMaxLogicalProcessorNumber < 1) {
> >> @@ -1795,12 +1843,20 @@ InitializeMpSupport (
> >>      }
> >>    }
> >>  
> >> -  Status = gBS->CreateEvent (
> >> -                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
> >> -                  TPL_CALLBACK,
> >> -                  ExitBootServicesCallback,
> >> -                  NULL,
> >> -                  &mExitBootServicesEvent
> >> -                  );
> >> -  ASSERT_EFI_ERROR (Status);
> >> +  if (mMpSystemData.NumberOfProcessors > 1) {
> >> +    CpuApHltLoopAddress = BASE_1MB - 1;
> >> +    Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS,
> >> +                    EFI_SIZE_TO_PAGES (sizeof (CPU_AP_HLT_LOOP)),
> >> +                    &CpuApHltLoopAddress);
> >> +    ASSERT_EFI_ERROR (Status);
> >> +
> >> +    Status = gBS->CreateEvent (
> >> +                    EVT_SIGNAL_EXIT_BOOT_SERVICES,
> >> +                    TPL_CALLBACK,
> >> +                    ExitBootServicesCallback,
> >> +                    (VOID *)(UINTN)CpuApHltLoopAddress,
> >> +                    &mExitBootServicesEvent
> >> +                    );
> >> +    ASSERT_EFI_ERROR (Status);
> >> +  }
> >>  }
> >> -- 
> >> 1.8.3.1
> >>
> 
> _______________________________________________
> edk2-devel mailing list
> [email protected]
> https://lists.01.org/mailman/listinfo/edk2-devel
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to