SMI Communication
The kernel driver communicates with the SMM handler via a shared buffer + software SMI. The buffer's physical address is published to NVRAM at boot time by DioProcessDxe.efi.
Software SMI is Platform-Specific
Software SMI is typically triggered by writing to I/O port 0xB2 (Intel APM_CNT). The exact behavior depends on the chipset; QEMU/OVMF emulates the standard PIIX4/ICH9 behavior.
NVRAM Variable
At DXE phase, DioProcessDxe.efi allocates a communication buffer and publishes its physical address to a runtime-accessible NVRAM variable so the OS can find it.
- • GUID:
{D10PR0C5-1337-4242-BEEF-CAFEBABE0002} - • Name:
DioProcessSmmBuffer - • Value: 64-bit physical address of the communication buffer
- • Attributes: BS+RT (Boot Service + Runtime Access)
Kernel-Side SMI Trigger
// kernelmode/DioProcess/DioProcessDriver/SMM/SmmCommunication.cpp
NTSTATUS TriggerSmi(SMM_COMM_BUFFER* Cmd) {
// 1. Read buffer address from NVRAM
ULONG64 BufferPa = ReadNvramU64(&kSmmBufferGuid, L"DioProcessSmmBuffer");
if (!BufferPa) return STATUS_NOT_FOUND;
// 2. Map buffer into kernel VA
PHYSICAL_ADDRESS Pa = { .QuadPart = BufferPa };
PVOID BufferVa = MmMapIoSpace(Pa, sizeof(*Cmd), MmNonCached);
if (!BufferVa) return STATUS_INSUFFICIENT_RESOURCES;
// 3. Copy command into shared buffer
RtlCopyMemory(BufferVa, Cmd, sizeof(*Cmd));
// 4. Trigger software SMI (OUT 0xB2, cmd)
__outbyte(0xB2, 0x00); // SMI command byte — SMM handler ignores value
// 5. Read response back
RtlCopyMemory(Cmd, BufferVa, sizeof(*Cmd));
// 6. Unmap
MmUnmapIoSpace(BufferVa, sizeof(*Cmd));
return STATUS_SUCCESS;
}SMM-Side Handler
// efi/DioProcessSmm/Smi.c
EFI_STATUS EFIAPI SmiHandler(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
) {
SMM_COMM_BUFFER *Cmd = (SMM_COMM_BUFFER *)CommBuffer;
if (Cmd->Signature != SIGNATURE_32('D','P','S','M')) {
return EFI_INVALID_PARAMETER;
}
switch (Cmd->Command) {
case CMD_READ_PHYS: Cmd->Status = HandleReadPhys(Cmd); break;
case CMD_WRITE_PHYS: Cmd->Status = HandleWritePhys(Cmd); break;
case CMD_TRANSLATE: Cmd->Status = HandleTranslate(Cmd); break;
case CMD_PING: Cmd->Status = STATUS_OK; break;
default: Cmd->Status = STATUS_INVALID_CMD; break;
}
return EFI_SUCCESS;
}Handler Registration (DXE Side)
The SMM driver registers itself with the SMM IPL when dispatched into SMRAM. It uses gSmst->SmiHandlerRegister with a unique GUID so the kernel side can target this specific handler if multiple SMM drivers coexist.
// efi/DioProcessSmm/SmmMain.c
EFI_STATUS EFIAPI DioProcessSmmEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
) {
EFI_HANDLE Handle = NULL;
return gSmst->SmiHandlerRegister(
SmiHandler,
&gDioProcessSmiHandlerGuid,
&Handle
);
}Serial Debug Output
When running under QEMU with OVMF, SMM debug prints appear on the serial console. This is the primary way to trace SMM initialization and command dispatch.
=[ DioProcess DXE ]=
[ DXE ] EFI_MM_COMMUNICATION2_PROTOCOL discovered
[ DXE ] Buffer allocated @ 0x7EFB1000
[ DXE ] NVRAM variable DioProcessSmmBuffer set
=[ DioProcess SMM ]=
=[ Ring -2 Memory Operations ]=
[ SMM ] SMM driver invoked by SMM IPL, initializing...
[ SMM ] SMI handler registered (GUID: D10PR0C5-...-0002)
[ SMM ] SMM driver has been initialized
[ SMM ] SMI fired — cmd=0x01 (READ_PHYS)
[ SMM ] ReadPhys 0x1000, 4096 bytes — OKImplementation
| Layer | Location |
|---|---|
| Kernel SMI trigger | kernelmode/.../SMM/SmmCommunication.cpp |
| SMM handler | efi/DioProcessSmm/Smi.c |
| DXE buffer publisher | efi/DioProcessDxe/DxeMain.c |