From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 5570081C8D for ; Mon, 7 Nov 2016 07:47:55 -0800 (PST) Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B3DF461BB2; Mon, 7 Nov 2016 15:47:57 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-116-129.phx2.redhat.com [10.3.116.129]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id uA7FluWQ025215; Mon, 7 Nov 2016 10:47:56 -0500 To: valerij zaporogeci References: From: Laszlo Ersek Cc: edk2-devel Message-ID: <660f572b-9945-34ef-54ae-5e434f19f473@redhat.com> Date: Mon, 7 Nov 2016 16:47:55 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.4.0 MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 07 Nov 2016 15:47:57 +0000 (UTC) Subject: Re: NotifyPpi description contradiction X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Nov 2016 15:47:55 -0000 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit 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