On 8 September 2017 at 10:48, Laszlo Ersek <[email protected]> wrote:
> On 09/08/17 10:53, Ard Biesheuvel wrote:
>> (cc'ing the trinity)
>>
>> On 7 September 2017 at 23:41, Laszlo Ersek <[email protected]> wrote:
>>> Repo:   https://github.com/lersek/edk2.git
>>> Branch: iommu_exit_boot
>>>
>>> This series is the result of the discussion under
>>>
>>>   [edk2] [PATCH 0/4] MdeModulePkg: some PCI HC drivers: unmap common
>>>                      buffers at ExitBootServices()
>>>   https://lists.01.org/pipermail/edk2-devel/2017-September/014099.html
>>>
>>> At ExitBootServices(), PCI and VirtIo drivers should only care about
>>> aborting pending DMA on the devices. Cleaning up PciIo mappings (which
>>> ultimately boil down to IOMMU mappings) for those aborted DMA operations
>>> should be the job of the IOMMU driver.
>>>
>>> Patches 01 through 03 clean up the AtaAtapiPassThru driver in
>>> MdeModulePkg a little bit, because at present, (a) it unmaps the buffers
>>> and disables BMDMA in the wrong order in its DriverBindingStop()
>>> function, (b) it doesn't abort pending DMA at ExitBootServices().
>>>
>>> This subset can be treated separately from the rest of the series, but I
>>> thought they belonged loosely together (given that AtaAtapiPassThru is
>>> used on QEMU's Q35 machine type).
>>>
>>> Patches 04 through 07 remove VIRTIO_DEVICE_PROTOCOL.UnmapSharedBuffer()
>>> calls from the VirtIo drivers' ExitBootServices() handlers.
>>>
>>> (The conversion of VirtioNetDxe to device addresses is still in progress
>>> -- Brijesh, when you submit v2 of that, under this approach, there is no
>>> need to change VirtioNetExitBoot() relative to current upstream, and you
>>> can use VirtioOperationBusMasterRead to map outgoing packets.)
>>>
>>> Patches 08 through 10 make OvmfPkg/IoMmuDxe track all mappings, and
>>> unmap all mappings (Read, Write, CommonBuffer) that are in effect when
>>> ExitBootServices() is called. It is ensured that PCI and VirtIo drivers
>>> abort pending DMA first, and IoMmuDxe clean up the mappings last.
>>>
>>
>> The patches look fine to me
>>
>> Reviewed-by: Ard Biesheuvel <[email protected]>
>
> Thank you!
>
>> Given that we are now depending on events signalled in an event
>> handler to be queued after all currently pending events,
>
> marking this (*)
>
>> I think we
>> need to explicitly agree that this behavior that needs to be
>> preserved, and documented somewhere, given that the UEFI spec does not
>> offer this guarantee.
>
> The condition that you describe under (*) *is* guaranteed in the UEFI spec.
>
> The *only* bit that is edk2 specific in the last patch is that we invoke
> gBS->SignalEvent() from the notification function of an event that
> *specifically* has type EVT_SIGNAL_EXIT_BOOT_SERVICES.
>
> If the event, whose notification function we were calling
> gBS->SignalEvent() from, was a plain EVT_NOTIFY_SIGNAL type event, then
> *nothing* in the last patch would be edk2-specific.
>
> * It is guaranteed by the UEFI spec that signaling an event group queues
> the notification functions for all not-yet-signaled events in that
> group, before the first notification function is invoked (regardless of
> the signaled events' TPLs). From "CreateEventEx()": "All events are
> guaranteed to be signaled before the first notification action is taken."
>
> * The UEFI spec guarantees that, within the same TPL, if an event is
> signaled that is not pending yet, the notify function will be queued
> after notify functions already queued on the same TPL. See "CreateEvent()":
>
>     Events exist in one of two states, “waiting” or “signaled.” When an
>     event is created, firmware puts it in the “waiting” state. When the
>     event is signaled, firmware changes its state to “signaled” and, if
>     EVT_NOTIFY_SIGNAL is specified, places a call to its notification
>     function in a FIFO queue. There is a queue for each of the “basic”
>     task priority levels defined in Section 7.1 (TPL_CALLBACK, and
>     TPL_NOTIFY). The functions in these queues are invoked in FIFO
>     order, starting with the highest priority level queue and proceeding
>     to the lowest priority queue that is unmasked by the current TPL. If
>     the current TPL is equal to or greater than the queued notification,
>     it will wait until the TPL is lowered via
>     EFI_BOOT_SERVICES.RestoreTPL().
>
> * gBS->SignalEvent() is valid to call at TPL_CALLBACK and TPL_NOTIFY
> levels (see "Table 23. TPL Restrictions"); in fact it is one of the few
> services that can even be called at TPL_HIGH_LEVEL (which is reserved
> for the platform firmware).
>
> The upshot is:
>
> (1) assume you have Event1, Event2, Event3, Event4 in event group
> EventGroupFoobarGuid
>
> (2) assume all events are EVT_NOTIFY_SIGNAL type
>
> (3) assume Event1 and Event2 have TPL_NOTIFY, Event3 and Event4 are
> TPL_CALLBACK
>
> (4) Assume Event5 is also of type EVT_NOTIFY_SIGNAL, not part of any
> event group, and has TPL_CALLBACK task prio level
>
> (5) Assume an agent running at TPL_APPLICATION creates an event
> temporarily, with type 0 (see the code example in CreateEventEx), and
> signals EventGroupFoobarGuid through it.
>
> (6) All of Event1, Event2, Event3 and Event4 move into the signaled
> state at once. NotifyFunction1 and NotifyFunction2 are queued in
> unspecified order in the TPL_NOTIFY queue, and Event3 and Event4 are
> queued in unspecified order in the TPL_CALLBACK queue.
>
> (7) Because our current TPL is TPL_APPLICATION, and signaled events of
> type EVT_NOTIFY_SIGNAL with higher TPLs exist, the SignalEvent() service
> will start dispatching them immediately.
>
> (8) Either NotifyFunction1 or NotifyFunction2 is called first, running
> at TPL_NOTIFY.
>
> (9) Assume that NotifyFunction2 signals Event5. (it doesn't matter if
> NotifyFunction2 is called before or after NotifyFunction1).
>
> NotifyFunction5 is not dispatched immediately (on the stack of
> SignalEvent()) because the current TPL, TPL_NOTIFY, is higher than or
> equal to, the TPL of Event5, which is TPL_CALLBACK. So NotifyFunction5
> is queued instead.
>
> Because NotifyFunction3 and NotifyFunction4 are already in the
> TPL_CALLBACK queue (in unspecified relative order), from step (6),
> NotifyFunction5 will be queued after both of them, in FIFO order.
>
> (10) If NotifyFunction1 has not been invoked yet, it is invoked now. It
> runs at TPL_NOTIFY. The TPL_NOTIFY queue becomes empty.
>
> (11) NotifyFunction3 and NotifyFunction4 are invoked in unspecified
> order. They both run at TPL_CALLBACK.
>
> (12) NotifyFunction5 is invoked. It runs at TPL_CALLBACK. The
> TPL_CALLBACK queue becomes empty.
>
> (13) SignalEvent() returns to the agent mentioned in (5).
>
>
> The one thing to remember, from the client programmer's POV, is that
> *any* event of type EVT_NOTIFY_SIGNAL can only be dispatched from within
> two service calls:
>
> - SignalEvent(), if the caller is running at a TPL strictly below the
> TPL of the event being signaled (directly or via event group GUID), and
>
> - RestoreTPL(), if the restored TPL falls strictly below the TPL of the
> event, and the event has already been signaled.
>
> IOW, SignalEvent() "calls or queues", and RestoreTPL "de-queues and
> calls, or does nothing".
>
>
> So, again, the only question that the UEFI spec does not clarify is
> whether we can call gBS->SignalEvent() specifically from an EBS
> notification function.
>
> The intent is likely just that, because:
>
>   EVT_SIGNAL_EXIT_BOOT_SERVICES
>     [...] This event is of type EVT_NOTIFY_SIGNAL [...]
>
> and, again, if we call SignalEvent from the NotifyFunction of a plain
> EVT_NOTIFY_SIGNAL event, then it's all covered by the spec already.
>
> My apologies about the wall of text. I probably over-explained it five-fold.
>

OK
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to