How to Write a UEFI SMI Module in AMI Aptio 4
In the previous tutorial, we explored UEFI Event modules and learned how to execute firmware code in response to a specific event. However, some firmware operations must remain available after the operating system has taken control, including selected power-management operations, hardware error handling, and OEM-specific platform management.
On x86 systems, System Management Mode (SMM) provides a privileged execution environment for such firmware operations. A System Management Interrupt (SMI) transfers control to firmware code running in SMM, allowing the platform to perform supported operations independently of ordinary operating-system execution.
This tutorial walks through the implementation of an EFITrainingSMI module using an AMI Aptio-style firmware development environment. Starting with the component definition and build configuration, we will implement the module’s dual-stage initialization flow, register a software SMI handler, and verify execution using a controlled hardware test.
The tutorial covers six source files, the roles of InitSmmHandler(), EFI_SMM_SW_DISPATCH_PROTOCOL, and dependency expressions, and the practical debugging techniques needed to troubleshoot SMM registration failures.
Scope and compatibility: The code follows a legacy AMI Aptio-style framework that provides InitSmmHandler() and the older EFI_SMM_SW_DISPATCH_PROTOCOL. These are not universal UEFI interfaces. Modern EDK II and PI-based implementations use different SMM or Management Mode interfaces, such as EFI_SMM_SW_DISPATCH2_PROTOCOL or EFI_MM_SW_DISPATCH_PROTOCOL. Adapt the code to the headers and dispatch model supported by your target firmware.
Because SMM executes with highly privileged access, develop and test this module only on hardware you own or are authorized to modify. Use a recoverable test platform, confirm that the selected SMI command is unused, and avoid flashing experimental firmware onto production systems.
🧠 1. Understanding SMM and Software SMIs #
1.1 What Is System Management Mode? #
System Management Mode is a dedicated x86 processor execution mode used primarily for platform firmware operations.
Its distinguishing characteristics include:
- Privileged execution: SMM can execute firmware code with access to resources protected from ordinary operating-system execution.
- SMRAM: System Management RAM contains SMM code and data. Properly configured platform hardware restricts ordinary software from accessing this protected memory.
- Synchronous execution relative to the interrupted CPU: The processor enters the SMM handler in response to an SMI, saves execution state, and resumes the interrupted context after the handler completes.
- Platform-level responsibilities: Firmware can use SMM for supported power-management, hardware-error, and OEM-specific operations.
An SMI is not masked by the normal interrupt-enable flag in the same way as an ordinary maskable interrupt. However, chipset configuration, processor state, and platform implementation influence when and how an SMI is serviced.
SMM should not be treated as an ordinary extension of the DXE environment. Boot Services and ordinary DXE protocol interfaces are not automatically valid inside an SMM handler. SMM code must use the interfaces and resources supported by its execution environment.
The principal architectural advantage is that selected firmware operations can remain under firmware control even while the operating system is running.
1.2 How a Software SMI Works #
A software SMI is one way to request SMM execution. On platforms implementing the conventional APM software-SMI interface, writing a command byte to I/O port 0xB2 can trigger a software SMI.
For example, the following conceptual sequence illustrates the process:
Software writes command 0xAA
|
v
I/O port 0xB2 receives the command
|
v
Platform generates a software SMI
|
v
SMM dispatcher identifies the registered handler
|
v
EFITrainingSwSmiFunction() executes
|
v
Processor resumes interrupted execution
The command value is associated with the registered software-SMI dispatch context. The SMM dispatcher uses that context to determine which callback should process the request.
The command value 0xAA is used throughout this tutorial as an example. It is not a universal or reserved-safe SMI number. A platform may already assign it to another handler, and the supported command range depends on the chipset and firmware implementation.
Do not assume that writing to port 0xB2 will invoke the same functionality on every motherboard. The trigger mechanism, SMI source configuration, and dispatcher behavior must be verified for the target platform.
1.3 AMI Aptio’s Dual-Stage Initialization Pattern #
The main difference between this example and an ordinary DXE driver is the use of InitSmmHandler().
In the framework assumed here, the image entry point delegates initialization to this helper. The helper determines the execution context and routes control to one of two functions.
DXE-side execution
EFITraining_SMI_Install()
|
v
InitSmmHandler()
|
v
NotInSmmFunction()
|
v
Return EFI_SUCCESS
SMM-side execution
EFITraining_SMI_Install()
|
v
InitSmmHandler()
|
v
InSmmFunction()
|
v
Locate software-SMI dispatch interface
|
v
Register EFITrainingSwSmiFunction()
This separation lets the example handle ordinary DXE-side initialization and SMM-side registration through a shared entry-point pattern.
The exact mechanics of the second execution depend on the AMI implementation. InitSmmHandler() is an AMI framework helper, not a standard UEFI function. Do not assume that every UEFI SMM driver executes its entry point twice.
1.4 The Two Protocols #
The example depends on two legacy AMI-compatible SMM protocols.
| Protocol | Purpose |
|---|---|
EFI_SMM_BASE_PROTOCOL |
Provides the framework’s SMM context-related interface used by the initialization model. |
EFI_SMM_SW_DISPATCH_PROTOCOL |
Registers a callback for a particular software-SMI input value. |
The software dispatcher provides the registration interface and a handle identifying the registered callback. Registration also allows the dispatcher to validate the requested command value.
In a modern PI implementation, the corresponding protocols and registration interfaces may differ. In particular, do not assume that a legacy EFI_SMM_SW_DISPATCH_PROTOCOL callback can be substituted directly into an EDK II EFI_SMM_SW_DISPATCH2_PROTOCOL or EFI_MM_SW_DISPATCH_PROTOCOL implementation.
📁 2. Module Structure and Required Files #
The EFITrainingSMI module contains six files:
| File | Responsibility |
|---|---|
EFITrainingSMI.cif |
Declares the component identity and source-file list. |
EFITrainingSMI.sdl |
Defines build switches, configuration tokens, paths, and firmware-volume integration. |
EFITrainingSMI.mak |
Defines the build rules, module GUID, entry point, and DEPEX section. |
EFITrainingSMI.dxs |
Expresses dependencies required before the module is dispatched. |
EFITrainingSMI.h |
Declares the software-SMI callback. |
EFITrainingSMI.c |
Implements the entry point, dual-stage initialization functions, registration logic, and handler. |
The directory layout is:
EFITraining/
└── SMI/
├── EFITrainingSMI.cif
├── EFITrainingSMI.sdl
├── EFITrainingSMI.mak
├── EFITrainingSMI.dxs
├── EFITrainingSMI.h
└── EFITrainingSMI.c
The CIF, SDL, and MAK files integrate the component into the AMI build system. The DXS file supplies the dependency expression. The C and H files contain the implementation and callback declaration.
For more background on how AMI firmware components are registered and integrated into the build system, see How to Write a UEFI DXE Driver in AMI Aptio 4.
🛠️ 3. Implementing the Module Step by Step #
Step 1: Create the CIF component definition #
The CIF file identifies the component to the AMI build system and lists the source files associated with it.
Example EFITrainingSMI.cif:
<component>
name = "EFITraining - SMI"
category = ModulePart
LocalRoot = "EFITraining\SMI"
RefName = "EFITrainingSMI"
[files]
"EFITrainingSMI.sdl"
"EFITrainingSMI.mak"
"EFITrainingSMI.dxs"
"EFITrainingSMI.c"
"EFITrainingSMI.h"
</component>
The main responsibilities are straightforward:
nameidentifies the component.categoryidentifies its component type.LocalRootspecifies the component’s source directory.RefNameestablishes its build-system reference.[files]lists the files belonging to the component.
The precise syntax and path conventions can vary across AMI Aptio generations. Validate the example against the CIF parser and existing component definitions in the target firmware source tree.
Step 2: Configure SDL tokens and firmware integration #
The SDL file defines whether the module is enabled, where its sources reside, how its build rules are generated, and which software-SMI command it handles.
Example EFITrainingSMI.sdl:
TOKEN
Name = "EFITrainingSMI_SUPPORT"
Value = "1"
TokenType = Boolean
TargetMAK = Yes
TargetH = Yes
Master = Yes
End
MODULE
File = "EFITrainingSMI.mak"
End
PATH
Name = "EFITraining_SMI_DIR"
Path = "EFITraining\SMI"
End
ELINK
Name = "$(BUILD_DIR)\EFITrainingSMI.ffs"
Parent = "FV_MAIN"
InvokeOrder = AfterParent
End
TOKEN
Name = "EFITrainingSMI_Number"
Value = "0xaa"
TokenType = Integer
TargetH = Yes
Range = "0...0FFh"
End
The first token enables the module. Setting EFITrainingSMI_SUPPORT to 1 includes the module in the intended build configuration.
The MODULE and PATH declarations establish the associated build rule and source directory. The ELINK declaration integrates the generated EFITrainingSMI.ffs firmware file into FV_MAIN, assuming that the target platform’s build configuration recognizes these names.
The second token defines the software-SMI command.
Because TargetH = Yes, the AMI build system can expose the token through the generated token.h file. The C source can then reference EFITrainingSMI_Number instead of hard-coding the value in multiple places.
This makes changing the test command easier to manage.
Important: Before selecting a command value, inspect the target firmware’s existing SMI registrations and the dispatcher’s supported range. Never assume 0xAA is available merely because it fits in a byte. A conflict can cause registration to fail or interfere with another platform function.
Step 3: Define the dependency expression #
The DXS file declares the protocols that the module requires before it can be dispatched.
Example EFITrainingSMI.dxs:
#include <Protocol\SmmBase.h>
#include <Protocol\SmmSwDispatch.h>
DEPENDENCY_START
EFI_SMM_BASE_PROTOCOL_GUID AND
EFI_SMM_SW_DISPATCH_PROTOCOL_GUID
DEPENDENCY_END
The dependency expression tells the firmware dispatcher that both protocols must be available before the module is dispatched according to the configured dependency model.
This can prevent the module from attempting SMM-related initialization before the required protocol interfaces are installed.
Without an appropriate dependency expression, protocol discovery may fail because a required interface is not yet available.
However, there is an important platform-specific detail: the dependency expression must be evaluated by a dispatcher that can resolve the referenced protocol GUIDs in the relevant phase. Merely adding a DXS file does not guarantee correct ordering.
In particular, the target AMI build must package this expression into the intended DEPEX section, and the selected protocols must be available to the dispatcher evaluating that section. Confirm this behavior in the firmware’s build output and existing SMM-capable module examples.
Step 4: Declare the software-SMI handler #
The header file declares the callback registered with the legacy software-SMI dispatcher.
Example EFITrainingSMI.h:
#ifndef _EFI_TRAINING_SMI_H_
#define _EFI_TRAINING_SMI_H_
VOID
EFITrainingSwSmiFunction(
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
);
#endif
The callback parameters follow the legacy EFI_SMM_SW_DISPATCH_PROTOCOL interface used by this example.
DispatchHandle identifies the registered dispatch instance. DispatchContext contains the software-SMI command context associated with the callback.
These declarations are specific to the legacy protocol. A modern handler registered through another SMM dispatch interface may have a different function signature and context structure.
Step 5: Implement the C source #
The C file contains four principal elements:
- Header and protocol declarations.
- The SMM-side registration function.
- The DXE-side initialization function.
- The shared entry point and software-SMI handler.
5.1 Include the required headers #
Example declarations for the legacy AMI environment:
#include "Efi.h"
#include "token.h"
#include <AmiLib.h>
#include <AmiDxeLib.h>
#include <Protocol\SmmBase.h>
#include <Protocol\SmmSwDispatch.h>
#include "EFITrainingSMI.h"
EFI_GUID gSwDispatchProtocolGuid =
EFI_SMM_SW_DISPATCH_PROTOCOL_GUID;
The includes provide the EFI types, generated SDL token, AMI library helpers, SMM protocol definitions, and local callback declaration.
This example assumes that the relevant headers and libraries exist in the target AMI source tree. Do not substitute headers from a different firmware framework without checking the protocol definitions and calling conventions.
5.2 Register the handler inside the SMM initialization path #
The registration function locates the software-SMI dispatcher and registers the callback for the configured input value.
Example InSmmFunction():
EFI_STATUS
InSmmFunction(
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_SMM_SW_DISPATCH_PROTOCOL *SwDispatch;
EFI_SMM_SW_DISPATCH_CONTEXT SwContext;
EFI_STATUS Status;
EFI_HANDLE Handle = NULL;
SwContext.SwSmiInputValue = EFITrainingSMI_Number;
Status = pBS->LocateProtocol(
&gSwDispatchProtocolGuid,
NULL,
(VOID **)&SwDispatch
);
if (EFI_ERROR(Status)) {
return Status;
}
Status = SwDispatch->Register(
SwDispatch,
EFITrainingSwSmiFunction,
&SwContext,
&Handle
);
return Status;
}
The registration sequence has four steps.
First, SwContext.SwSmiInputValue receives the command defined by the SDL token.
Second, the code locates the software-SMI dispatcher.
Third, Register() associates the input value with EFITrainingSwSmiFunction() and returns a dispatch handle.
Finally, the function returns the registration status so that initialization errors can be detected.
Framework-specific caveat: The use of pBS->LocateProtocol() is retained from the AMI-style example, whose initialization libraries and legacy protocol model determine how this lookup is supported. Do not assume that ordinary DXE Boot Services can be used from arbitrary SMM code.
For a modern PI SMM implementation, the corresponding operation typically uses the SMM System Table’s protocol-location service, together with the appropriate SMM dispatch protocol. Follow the exact implementation model provided by the target firmware.
For improved diagnostics, production-quality initialization should also log registration failures through a debugging mechanism known to be safe in the applicable execution environment.
5.3 Implement the DXE-side placeholder #
The DXE-side function provides a place for work that must occur outside SMM.
For this tutorial, no such work is required.
EFI_STATUS
NotInSmmFunction(
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
return EFI_SUCCESS;
}
The function returns success without performing additional operations.
Keeping it separate makes the dual-stage execution model explicit. If the module later needs DXE-side initialization, that work can be added here without moving SMM-specific registration into the wrong execution context.
5.4 Implement the shared entry point #
The entry point initializes the AMI library and delegates execution-context detection to InitSmmHandler().
EFI_STATUS
EFITraining_SMI_Install(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
InitAmiLib(ImageHandle, SystemTable);
TRACE((
TRACE_ALWAYS,
"EFI Training SMI install/reg...\n"
));
return InitSmmHandler(
ImageHandle,
SystemTable,
InSmmFunction,
NotInSmmFunction
);
}
InitAmiLib() initializes the AMI library environment used by the sample, including the helpers referenced in the module.
TRACE() emits a diagnostic message when supported by the configured debug infrastructure.
InitSmmHandler() then routes control to the appropriate function for the framework’s execution context.
The intended behavior is:
NotInSmmFunction()handles the DXE-side path.InSmmFunction()performs software-SMI registration through the SMM-side path.
This division is the central architectural pattern of the example.
5.5 Implement a minimal software-SMI handler #
The callback must remain short because it runs in a privileged firmware context and can affect system latency.
For this test, we use a small counter and write its value to legacy POST-code I/O port 0x80.
STATIC UINT8 mPostCodeCounter = 0;
VOID
EFITrainingSwSmiFunction(
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_SW_DISPATCH_CONTEXT *DispatchContext
)
{
mPostCodeCounter++;
IoWrite8(0x80, mPostCodeCounter);
}
Each successfully handled SMI increments the counter and writes its current value to the port. On a compatible system with a connected POST-code display, this may provide a visible indication that the handler executed.
The counter wraps from 0xFF to 0x00 because it is an 8-bit value.
This implementation intentionally avoids reading port 0x80 to obtain the previous value. The behavior of reading that legacy port is platform-dependent, and the returned value is not guaranteed to represent the last code written.
Port 0x80 is also not a universal debugging interface. If the target platform uses it for another purpose, choose a safer, platform-supported diagnostic mechanism.
The handler performs no allocation, logging, protocol discovery, or lengthy computation. Its only purpose is to demonstrate callback execution.
🧱 4. Configure the MAK Build Rules #
The MAK file defines the build target, the firmware file GUID, the entry point, the object list, and the dependency-expression section.
Example EFITrainingSMI.mak:
all : EFITrainingSMI
EFITrainingSMI : $(BUILD_DIR)\EFITrainingSMI.mak EFITrainingSMIBin
$(BUILD_DIR)\EFITrainingSMI.mak : \
$(EFITraining_SMI_DIR)\EFITrainingSMI.cif \
$(EFITraining_SMI_DIR)\EFITrainingSMI.mak \
$(BUILD_RULES)
$(CIF2MAK) $(EFITraining_SMI_DIR)\EFITrainingSMI.cif \
$(CIF2MAK_DEFAULTS)
EFITrainingSMIBin : $(AMIDXELIB) $(AMICSPLib)
$(MAKE) /$(MAKEFLAGS) $(BUILD_DEFAULTS) \
/f $(BUILD_DIR)\EFITrainingSMI.mak all \
NAME=EFITrainingSMI \
MAKEFILE=$(BUILD_DIR)\EFITrainingSMI.mak \
OBJECTS="$(BUILD_DIR)\$(EFITraining_SMI_DIR)\EFITrainingSMI.obj" \
GUID=80A1BB91-2036-46DD-9CC8-97A1F2F7E5BC \
ENTRY_POINT=EFITraining_SMI_Install \
TYPE=BS_DRIVER \
DEPEX1=$(EFITraining_SMI_DIR)\EFITrainingSMI.dxs \
DEPEXITYPE=EFI_SECTION_DXE_DEPEX \
COMPRESS=1
The example preserves the AMI-style build conventions supplied for this module. Exact macro names, continuation rules, and dependency packaging must match the selected AMI build environment.
Several settings are particularly important.
Module GUID #
The GUID identifies the generated firmware file.
The example uses a GUID for illustration. Generate a unique GUID for a new component rather than copying an identifier already used by another firmware file in the same platform.
Entry point #
ENTRY_POINT=EFITraining_SMI_Install identifies the C function called when the module is dispatched through the configured build model.
Ensure that the entry-point name matches the definition in EFITrainingSMI.c.
Dependency expression #
Two settings connect the DXS file to the firmware image:
DEPEX1=$(EFITraining_SMI_DIR)\EFITrainingSMI.dxs
DEPEXITYPE=EFI_SECTION_DXE_DEPEX
DEPEX1 supplies the dependency-expression source, and DEPEXITYPE identifies the intended section type.
These settings do not independently prove that the SMM registration sequence is correctly ordered. The final FFS file must contain a dependency expression appropriate for its dispatcher and execution model.
After building, inspect the generated firmware file and build logs to confirm that the expected dependency section was produced and included.
Module integration #
The ELINK declaration in SDL integrates the resulting EFITrainingSMI.ffs into FV_MAIN, assuming that the target build configuration and firmware-volume layout support this arrangement.
The .dxs file, MAK settings, and final firmware packaging must be considered together. Adding only one of them is insufficient if the dependency information does not reach the final image.
🔧 5. Compile, Integrate, and Verify the Module #
5.1 Compile the firmware image #
Follow the normal build procedure for the target AMI Aptio source tree.
- Place the
SMIdirectory under the intendedEFITrainingsource directory. - Register the component with the applicable CIF and SDL build configuration.
- Confirm that
EFITrainingSMI_SUPPORTis set to1. - Check that the generated token header contains
EFITrainingSMI_Number. - Run the appropriate full or incremental build.
- Inspect the build logs for compilation, linking, dependency-expression, and firmware-volume integration errors.
- Confirm that
EFITrainingSMI.ffsis included in the intended firmware volume.
If the module is not compiled at all, investigate the component registration and support token before debugging the SMM handler.
If compilation succeeds but the module does not initialize, verify the entry point, firmware-file integration, dependency expression, and protocol availability.
5.2 Prepare a recoverable test environment #
Before flashing or testing the module, confirm that you have:
- A platform-compatible firmware image and verified recovery method.
- Access to serial debugging or another reliable diagnostic channel.
- Confirmed that the sample software-SMI command does not conflict with an existing handler.
- A supported method of issuing the test command.
- A means of recovering the machine if the firmware image fails to boot.
Do not assume that a successful build guarantees a bootable image. Firmware-volume layout, dependency evaluation, platform-specific configuration, and image-signing requirements can all affect whether the module is accepted and dispatched.
For general background on firmware-volume structure and the role of SMI-related interfaces in firmware access, see UEFI Firmware Volumes: FV Headers, FFS, HOBs, and Access.
5.3 Verify registration before triggering the SMI #
The first verification target is the initialization path, not the handler itself.
A successful registration should establish that:
- The module entry point is dispatched.
- The expected diagnostic trace is emitted.
- The framework selects the intended initialization path.
- The SMM-side function obtains the software-SMI dispatcher.
- The registration call returns
EFI_SUCCESS.
If registration fails, record the returned EFI status and investigate protocol availability, the SMI command range, potential command collisions, and any framework-specific restrictions.
Do not proceed on the assumption that registration succeeded simply because the module compiled.
5.4 Trigger a software SMI in a controlled environment #
On a compatible legacy x86 test platform, software-SMI mechanisms can use the APM command port 0xB2.
The following is a conceptual example of the write operation:
Port: 0xB2
Command: 0xAA
A legacy DOS debugging utility may support syntax similar to:
debug
-o b2 aa
-q
The precise command syntax depends on the debugger and environment. The example assumes that the utility supports direct I/O port access and that the platform maps the command to the intended software-SMI source.
On Windows, ordinary user-mode applications generally cannot directly issue arbitrary I/O port operations. A compatible diagnostic utility may rely on a kernel driver or another privileged mechanism, and modern security policies can block such access.
Use only a tool and method appropriate for an authorized test machine. A raw port write is not a portable UEFI command and should not be used on an unknown production system.
5.5 Confirm handler execution #
If the test platform supports the legacy POST-code port, an attached diagnostic display may show the counter emitted by the callback.
The expected observation is a change in the POST-code output after the test command is issued.
This is useful evidence, but it is not conclusive in isolation. Port 0x80 behavior depends on the platform, and a visible code does not prove that all initialization and registration details are correct.
A stronger verification combines several independent observations:
| Check | Expected result |
|---|---|
| Build log | Module compilation and packaging succeed. |
| Firmware integration | The FFS file is present in the intended firmware volume. |
| Entry-point trace | The initialization message is observed. |
| Registration status | Register() returns EFI_SUCCESS. |
| Trigger test | The compatible software-SMI mechanism invokes the registered callback. |
| Handler diagnostic | The expected test counter or approved diagnostic output changes. |
The test should be repeated under controlled conditions to establish reproducibility. Avoid interpreting a single POST-code value as proof of correct SMM behavior.
⚠️ 6. Common Errors and Debugging Strategies #
Support token is disabled #
If EFITrainingSMI_SUPPORT is set to 0, the build may omit the module entirely.
Check the active SDL configuration and inspect the build log to confirm that the expected target was generated.
The software-SMI command is already in use #
The test command 0xAA is an example, not a reserved-free value.
A platform may already associate it with chipset management, an OEM feature, or another registered handler. A collision can cause registration to fail or produce an unintended interaction.
Inspect existing dispatch registrations and use a command value verified to be available on the specific test platform. The dispatcher may also enforce a maximum input value that is narrower than the theoretical byte-sized range.
The DXS dependency is missing or ineffective #
If the dependency source is omitted from the build, or the final firmware file contains no valid dependency section, the module may be dispatched at an inappropriate time.
Check both the MAK settings and the packaged FFS file. Confirm that the relevant protocol GUIDs are resolvable by the dispatcher evaluating the dependency expression.
A source-level DXS file alone is not evidence that the final module has the correct dependency.
Registration is performed in the wrong execution context #
The example places registration inside InSmmFunction() because it is intended for the SMM-side initialization path.
Moving that operation into NotInSmmFunction() changes the execution context and can invalidate the assumptions behind the legacy SMM dispatcher interface.
For modern SMM implementations, use the supported SMM system table and dispatch protocol rather than reproducing the legacy dual-stage pattern blindly.
DXE services are used inside the handler #
An ordinary SMM handler must not assume that DXE Boot Services are valid in SMM.
Protocol lookup, memory allocation, and other service operations must use interfaces appropriate to the actual execution environment. Some AMI libraries provide compatibility abstractions, but those abstractions are framework-specific.
The minimal callback in this article avoids such calls entirely.
The handler takes too long #
SMI handling affects system responsiveness and can have platform-wide consequences depending on how the firmware coordinates processors and services the interrupt.
Keep handlers short. Avoid unbounded loops, long delays, unnecessary allocation, verbose output, and blocking operations.
If more complicated processing is required, design a bounded operation that respects the SMM execution model and validate its timing on the target platform.
The firmware file or GUID is incorrect #
A duplicated firmware-file GUID can create integration problems, while an invalid section or entry-point configuration may prevent the module from loading or executing.
Use a unique GUID for the component, verify the final file structure, and compare the build rules against a known-good module built for the same firmware branch.
Warm rebooting produces inconsistent observations #
Changes to firmware images, platform configuration, or SMM-related state can behave differently across reset types.
Use the target platform’s documented restart and recovery procedure, and perform the appropriate cold-start verification when required. Do not assume that every modification is fully reset by a warm reboot.
Windows cannot trigger the SMI #
A command-line utility’s ability to run does not mean it can access privileged hardware interfaces.
Confirm that the diagnostic utility supports the intended I/O operation, that the platform allows it, and that the chosen SMI mechanism is implemented by the target firmware. Do not disable system protections or bypass security controls on a production machine to force the test to work.
📋 7. Implementation Checklist #
Before considering the sample complete, verify the following items.
-
EFITrainingSMI.cifregisters the component and lists its files. -
EFITrainingSMI.sdlenables the module and defines the command token. -
EFITrainingSMI.dxsdeclares the intended dependencies. - The final MAK rules include the required dependency section.
-
EFITrainingSMI.hmatches the callback interface expected by the legacy dispatcher. - The C entry point initializes the AMI library and calls
InitSmmHandler(). - Registration runs in the intended SMM-side path.
- The configured software-SMI command is available on the test platform.
- The build output confirms successful compilation and firmware-volume integration.
- The handler performs only short, bounded, test-safe operations.
- Registration status and handler execution are verified independently.
- A tested firmware recovery method is available.
✅ Conclusion: From a DXE Module to an SMM Software-SMI Handler #
The EFITrainingSMI example demonstrates the main components of a legacy AMI Aptio-style software-SMI module: component registration, SDL configuration, dependency-expression packaging, dual-stage initialization, and callback registration.
The execution path can be summarized as follows:
EFITraining_SMI_Install()
|
v
InitSmmHandler()
|
+-----------------------------+
| |
v v
NotInSmmFunction() InSmmFunction()
|
v
Locate SW dispatcher
|
v
Register handler
|
v
External SMI trigger
|
v
EFITrainingSwSmiFunction()
The essential implementation details are:
- Use the CIF and SDL files to register and configure the component.
- Ensure the DXS dependency is correctly incorporated into the final firmware file.
- Keep SMM-specific registration in the appropriate initialization path.
- Use the dispatch protocol and handler signature supported by the target firmware.
- Keep the handler short, validate registration, and test only on a recoverable platform.
Most importantly, distinguish the AMI framework used by this example from modern PI SMM and Management Mode implementations. The same architectural principles remain useful, but the precise protocol names, function signatures, dependency model, and initialization path must match the target firmware.
A successful SMI module is not simply a C function that executes after an interrupt. It is a firmware component whose build integration, dispatch dependencies, execution context, and hardware interactions must all be correct.