public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: valerij zaporogeci <vlrzprgts@gmail.com>
Cc: edk2-devel <edk2-devel@ml01.01.org>
Subject: Re: NotifyPpi description contradiction
Date: Mon, 7 Nov 2016 16:47:55 +0100	[thread overview]
Message-ID: <660f572b-9945-34ef-54ae-5e434f19f473@redhat.com> (raw)
In-Reply-To: <CANPuzFz1Ycy435V10L+n=7Aa6W7H+XTeFq5e+7htE4-__BVSew@mail.gmail.com>

On 11/06/16 01:43, valerij zaporogeci wrote:
> Hi.
> I have two quotes from Pei spec (Vol1) related to the notification
> (callback) invocation logic. Tell me, which one tells the truth. They
> are obviously contradictive.
> Quote (page 26, description of NotifyPpi service):
> Description
> This service enables PEIMs to register a given service to be invoked 
> when another service is installed or reinstalled. This service will
> fire notifications on PPIs installed prior to this service 
> invocation. This is different behavior than the
> RegisterProtocolNotify of UEFI2.0, for example 
> EFI_PEI_NOTIFY_DESCRIPTOR is defined in “PEIM Descriptors” on page
> 113.
> Quote (page 115-116, EFI_PEI_NOTIFY_DESCRIPTOR description):
> Description
> EFI_PEI_NOTIFY_DESCRIPTOR is a data structure that is used by a PEIM 
> that needs to be called back when a PPI is installed or reinstalled.
> The notification is similar to the RegisterProtocolNotify() function
> in the UEFI 2.0 Specification.
> ...
> Following is an example of the notification use model for 
> EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI. In this example, a PEIM
> called SamplePeim executes early in the PEI phase before main memory
> is available. However, SamplePeim also needs to create some large
> data structure later in the PEI phase. As such, SamplePeim has a NULL
> depex, but after its entry point is processed, it needs to call 
> NotifyPpi() with a EFI_PEI_NOTIFY_DESCRIPTOR, where the notification
> descriptor includes the following:
> • A reference to EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI
> • A reference to a function within this same PEIM called SampleCallback
> When the PEI Foundation finally migrates the system from temporary
> to permanent memory and installs the 
> EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI, the PEI Foundation assesses
> if there are any pending notifications on this PPI. After the PEI 
> Foundation discovers the descriptor from SamplePeim, the PEI 
> Foundation invokes SampleCallback.
> 
> So, when SamplePeim invokes NotifyPpi on
> EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI, its callback (SampleCallback)
> will be called if EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI is ALREADY
> installed at this time, or if EFI_PEI_PERMANENT_MEMORY_INSTALLED_PPI
> will be installed later, AFTER this notification registration for it
> by NotifyPpi?

Both.

> It's obvious, that the answer is the second variant - when the PPI in
> question will be installed AFTER the notification registration on it.
> As it is seen on the quoted example. But then, the first quote,
> NotifyPpi description, says absolutely opposite and very misleading
> thing.

No, it doesn't say the opposite.

> And yet, in light of the above, NotifyPpi description also lacks
> explanation how it would behave if a PEIM tries to register a
> notification for the PPI already installed. What should happen in this
> case? Should Peim's callback still be called (when if so)? Or should
> NotifyPpi return an error, or success without any action? I believe,
> it should register it and call only for the other later installed
> instances with the same GUID. Is it a right guess?

RegisterProtocolNotify() is tied to an event.

- Events are UEFI-only, in PEI there is no corresponding concept.

- The event that is passed to RegisterProtocolNotify() is signaled when
  the protocol in question is installed or reinstalled.

- The event is not signaled right when RegisterProtocolNotify() returns.

- When the event is signaled (manually, or by the system in response to
  protocol install / reinstall), the notify function associated with
  the event is enqueued at the TPL that was specified at event creation
  time.

- When the notification function executes, it calls LocateProtocol() in
  a loop, to find all new (or reinstalled) instances of the protocol in
  question. Each call of LocateProtocol() returns one as yet unseen
  instance (if any), until the loop terminates upon seeing
  EFI_NOT_FOUND.

  (Similarly: LocateHandle() with SearchKey==ByRegisterNotify.)

  The state object that tracks the already processed / seen
  protocol instances (or handles) is pointed-to by the Registration
  pointer (or the SearchKey pointer). It is an opaque system-internal
  object that was allocated and returned by RegisterProtocolNotify().

  This internal object carries state not just between adjacent
  iterations of the loop in question, but also between separate calls
  of the event notification function. This way the callback only has to
  process as yet unseen (preexistent, freshly installed, or
  reinstalled) protocol instances / handles.

- To see why a loop is necessary per callback invocation, consider the
  TPL. An agent that installs multiple instances of the same protocol
  (on separate handles of course) might do that running at a task prio
  level that is higher than or equal to the TPL of the notification
  function. (Protocol Handler Services are permitted to be called at
  levels up to and including TPL_NOTIFY.) In this case the event is
  signaled several times, but only the first signal makes a real
  difference; the event enters the signaled state and its notification
  function is enqueued. When the TPL is finally lowered sufficiently,
  the notification function is called only once -- and then it has to
  process all the new / reinstalled protocol instances (or handles).

- Because at the time of calling RegisterProtocolNotify() the event is
  not automatically signaled, and protocol instances from earlier could
  exist, the programmer is responsible for calling SignalEvent()
  manually, right after RegisterProtocolNotify(). This will result in
  one call to the event notification function (assuming an appropriate
  TPL -- otherwise, it'll happen when the TPL is lowered), and then the
  function will have to process *all* preexistent protocol interfaces
  in said loop.

- When you close the event, the protocol notify that signals it goes
  away automatically, along with the Registration tracker.

Now let's consider the spec quotes.

* NotifyPpi() is similar to RegisterProtocolNotify() in that the former
allows the programmer to register callbacks for PPI installations and
reinstallations.

This is why the quote on pages 115-116 is correct about
RegisterProtocolNotify() and NotifyPpi() being similar.

* NotifyPpi() is dissimilar to RegisterProtocolNotify() in two facets:

- there are no events in PEI -- from which the difference follows that
NotifyPpi() fires immediately for preexistent PPIs, and

- there is no Registration object for tracking processed instances of a
given PPI GUID. Instead, LocatePpi() takes a parameter called Instance
(of type UINTN):

    PEI manages the interface set by maintaining a partial order on the
    interfaces such that the Instance of the interface, among others,
    can be traversed. [...] The Instance value designates when a PPI
    was installed. For an implementation that must reference all
    possible manifestations of a given GUID-named PPI, the code should
    invoke LocatePpi() with a monotonically increasing Instance number
    until EFI_NOT_FOUND is returned.

So you would maintain an UINTN variable (STATIC or dynamically
allocated) to carry this information from one invocation of the callback
to the next.

These two reasons are why the quote on page 26 is correct about
RegisterProtocolNotify() and NotifyPpi() being different.

> And also, regarding the fact there might be multiple instances of the
> particular PPI (with the same Guid). Did I understood properly, that
> with this model, since Peim registers for notification by the GUID,
> this means that on every instance installation and reinstallation of
> the PPI with the same GUID, Peim's callback will and should be called?
> Thank you for possible answer.

For preexistent instances of a PPI, NotifyPpi() will fire immediately,
once in total, for all preexistent instances cumulatively.

For instances of a PPI installed (or reinstalled) with the notification
already in place, it depends on EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK
vs. EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH. With the former, you get a
callback once per install / reinstall, on the stack of the InstallPpi()
call. With the latter, you can get a single callback for a batch of PPI
instances, when the PEIM that called InstallPpi() -- possibly several
times -- exits its entry point function and returns to the PEI core.

However, you can (and should) write the callback function such that this
distinction doesn't matter. Again, write a loop with LocatePpi() in the
loop body, and use a static (or dynamically allocated) UINTN Instance
counter, for carrying the "seen vs. unseen" boundary from one iteration
to the next, possibly across separate invocations of the function.

(Corrections are welcome, obviously!)

Thanks
Laszlo



  reply	other threads:[~2016-11-07 15:47 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-11-06  0:43 NotifyPpi description contradiction valerij zaporogeci
2016-11-07 15:47 ` Laszlo Ersek [this message]
2016-11-07 21:04   ` valerij zaporogeci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=660f572b-9945-34ef-54ae-5e434f19f473@redhat.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox