[edk2-devel] [edk2-discuss] SmmSwDispatch2Protocol - Registering an SMI - Can't find protocol with OVMF

Laszlo Ersek lersek at redhat.com
Thu Oct 1 17:10:35 UTC 2020


Hi,

(cross-posting to edk2-devel, comments below)

On 09/29/20 14:32, Simon McMillan wrote:
> Trying to register a SMI in my custom SMM module I'm running into
> issues.
>
> Before registering the SMI, the code looks for the specific protocol
> this way:
> 	Status = gSmst->SmmLocateProtocol(
> 			&gEfiSmmSwDispatch2ProtocolGuid,
> 			NULL,
> 			(VOID**)&SwDispatch
> 		);
> 	DEBUG ( (EFI_D_INFO, "SmmSwDispatch2Protocol Status: 0x%x\n", Status) );
>  	ASSERT_EFI_ERROR(Status);
>
> Unfortunately, while starting OVMF, boot hangs and the following
> message appears:
> 	SmmSwDispatch2Protocol Status: 0xE
> 	ASSERT_EFI_ERROR (Status = Not Found)
>
> Adding gEfiSmmSwDispatch2ProtocolGuid to the Depex section of my INF
> file doesn't solve the issue, in fact it prevents the custom code from
> being loaded at all.
>
> For info in OVMF, I simply added a reference to the inf file in the
> DSC file, so that my custom code is built along OVMF:
>
> 	custom_code/custom_code.inf
>
> OVMF is built with the following options:
> 	build -DSMM_REQUIRE
>
> Then the SMM module is converted to the right format and injected
> manually with UEFITool.
>
> Would anyone be of help with this situation? Or could give me an
> advice of what would be wrong or missing?

OVMF does not contain an implementation of the
EFI_MM_SW_DISPATCH_PROTOCOL.

While OVMF does handle a particular software MMI, that handling occurs
via a root MMI handler (in "OvmfPkg/CpuHotplugSmm") that watches out for
a particular command port value.

When I reviewed using a root MMI handler vs. EFI_MM_SW_DISPATCH_PROTOCOL
in last October, the latter seemed like a needless complication to me.
Mike, Jiewen and others provided many helpful explanations, and I
decided that EFI_MM_SW_DISPATCH_PROTOCOL was unjustified for OVMF.
Below, I'll try to rehash and extend parts of those discussions.

Considering EFI_MM_SW_DISPATCH_PROTOCOL, OVMF would have had to produce
that protocol from a chipset (aka "silicon") driver. And for actually
handling the CPU hotplug MMI, OVMF would have to consume
EFI_MM_SW_DISPATCH_PROTOCOL in a different -- not silicon, but
"platform" -- driver, and register an MMI handler through
EFI_MM_SW_DISPATCH_PROTOCOL.

However, for OVMF and QEMU, there is no practical distinction between
"chipset" (= "silicon") and "platform". The EFI_MM_SW_DISPATCH_PROTOCOL
implementation would be a thick wrapper on top of IO port 0xB2,
providing a more or less useless software abstraction.

- The only small advantage would be that EFI_MM_SW_DISPATCH_PROTOCOL
  would permit the caller to ask for a new (previously not agreed-upon)
  "SwMmiInputValue" (via ((UINTN)-1)), eliminating the need for
  coordination wrt. the 0xB2 IO port values.

  This was not a compelling feature, as upstream OVMF was about to get
  its first own MMI handler. Additionally, the command port value was
  going to have to be agreed upon between the OS (QEMU would generate
  the ACPI payload for the OS, for triggering the MMI) and the firmware.
  For such "public" MMIs, the generation of "SwMmiInputValue" by
  EFI_MM_SW_DISPATCH_PROTOCOL.Register() is not useful anyway.

- Furthermore, there was a *big* disadvantage -- the registered
  "DispatchFunction" would have to be called back with
  EFI_MM_SW_CONTEXT. And filling in  EFI_MM_SW_CONTEXT.SwMmiCpuIndex --
  "the 0-based index of the CPU which generated the software MMI" -- is
  both quite useless [*], and very challenging on OVMF / QEMU [**].

  [*] It might tell us what VCPU was executing the ACPI GPE handler in
      the OS's AML interpreter (ultimately raising the VCPU hotplug
      MMI), but what is that knowledge good for?

  [**] To refer forward to the QncSmmDispatcher module as an example: in
       that driver, "SwSmiCpuIndex" is laboriously determined in
       SwGetBuffer(), by checking which CPU's save state map indicates
       that *that* CPU was interrupted in the particular IO port write
       instruction that triggered the SMI.

       But on QEMU/KVM, this kind of information is not even available.
       The SmmCpuFeaturesReadSaveStateRegister() function in
       "OvmfPkg/Library/SmmCpuFeaturesLib/SmmCpuFeaturesLib.c" returns
       constant EFI_NOT_FOUND, for EFI_SMM_SAVE_STATE_REGISTER_IO.
       QEMU/KVM does not describe the interrupted OUT instruction in the
       save state map, and so the method seen in Quark's SwGetBuffer()
       couldn't work.

So, quite a few complications, for little use.

In the edk2-platforms tree, the directory

  Silicon/Intel/QuarkSocPkg/QuarkNorthCluster/Smm/DxeSmm/QncSmmDispatcher

contains a driver that produces EFI_MM_SW_DISPATCH_PROTOCOL (still using
the older / original name EFI_SMM_SW_DISPATCH2_PROTOCOL). When I
reviewed this driver, I formed the opinion that it wasn't really
"silicon-specific" -- I felt that it should be turned into a "universal"
IA32/X64 driver. The responsibilities of the driver (in relation to
EFI_MM_SW_DISPATCH_PROTOCOL) seem to be:

(1) It has to install a root MMI handler with
    gMmst->MmiHandlerRegister().

(2) It must fetch the Command and Data port values from the *same*
    chipset registers that the EFI_MM_CONTROL_PROTOCOL.Trigger()
    function writes. The location and nature of this register block is
    about the only "silicon-specific" bit, IMO.

(3) In the root MMI handler, the driver must look up the fetched Command
    value against the dictionary that is managed by Register() /
    UnRegister().

(4) Upon a match, the driver must call the callback associated with
    Command, and also pass Data to it (coming from
    EFI_MM_CONTROL_PROTOCOL.Trigger() via IO port 0xB3).

(5) EFI_MM_SW_DISPATCH_PROTOCOL.Register() must never hand out such a
    Command value (a.k.a. "SwMmiInputValue"), for ((UINTN)-1), that
    equals the value that EFI_MM_CONTROL_PROTOCOL.Trigger() stores for
    (CommandPort==NULL).

    Otherwise, a Communicate request could be mistaken for *that* SW
    MMI, when SmmEntryPoint() [MdeModulePkg/Core/PiSmmCore/PiSmmCore.c]
    invokes the root MMI handler registered in (1), after processing the
    Communicate request.

    This (ie., the agreement between EFI_MM_CONTROL_PROTOCOL and
    EFI_MM_SW_DISPATCH_PROTOCOL) is maybe the 2nd silicon-specific point
    that the EFI_MM_SW_DISPATCH_PROTOCOL driver has to take into
    account.

If such a generic driver existed, then we could perhaps include it
verbatim in the OVMF platform DSC / FDF files. If someone really needed
EFI_MM_SW_DISPATCH_PROTOCOL specifically.

Until then, I suggest registering another root MMI handler with
gMmst->MmiHandlerRegister(), like "OvmfPkg/CpuHotplugSmm" does. In the
handler, filter for a command port value that is strictly greater than
ICH9_APM_CNT_CPU_HOTPLUG (4).

See commit 17efae27acaf ("OvmfPkg/CpuHotplugSmm: introduce skeleton for
CPU Hotplug SMM driver", 2020-03-04).

Manual arbitration between the various values for command port 0xB2 is
necessary, accordingly. In practice this should not be a problem, as
OVMF is open source; just be prepared that new Command values upstreamed
to OVMF in the future might require you to pick a different Command
value for your out-of-tree driver.

Picking a value greater than ICH9_APM_CNT_CPU_HOTPLUG (4) will also
ensure that your root MMI handler filter out the "default" command port
value (namely zero) that OvmfPkg/SmmControl2Dxe uses in the Trigger()
implementation, when SmmCommunicationCommunicate()
[MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c] invokes Trigger() with
(Command==NULL), for submitting a Communicate request.

Thanks
Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#65809): https://edk2.groups.io/g/devel/message/65809
Mute This Topic: https://groups.io/mt/77245237/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-





More information about the edk2-devel-archive mailing list