SMM trong UEFI: System Management Mode từ góc debug
SMM không phải phase tuyến tính mà là isolated execution mode. Hiểu SMI trigger, SMRAM, handler timing, SmmReadyToLock và debug SetVariable không lưu được.
SMM: tại sao firmware engineer cần biết?
Có một loại bug report mà tôi thấy khá thường trong công việc custom BIOS cho POS: khách hàng báo “máy bán hàng lạ lắm, cài đặt hôm nay đúng nhưng sáng mai lại về mặc định”. Họ nghi cái gì đó reset tự động hoặc có virus. Thực ra không phải vậy.
Phần lớn những case đó dẫn về SMM, hoặc chính xác hơn là đường đi từ Setup UI tới SPI flash đi qua SMM và bị chặn ở đâu đó mà không có error message nào rõ ràng.
Nếu bạn đang làm BIOS và gặp một trong những tình huống này:
- BIOS setting lưu xong nhưng sau reboot quay về cũ
- Flash update BIOS từ OS thành công nhưng BIOS không thay đổi
- Một số service firmware không hoạt động sau khi OS đã boot
SetVariable()trảEFI_ACCESS_DENIEDhoặcEFI_WRITE_PROTECTED
Rất có khả năng vấn đề nằm trong SMM, System Management Mode.
SMM không phải là một thứ xa lạ học thuần lý thuyết. Đây là layer mà nhiều platform dùng để bảo vệ việc ghi NVRAM/SPI flash, xử lý một số event phần cứng, và thực thi security policy. Khi nó fail hoặc bị cấu hình sai, triệu chứng thường rất mơ hồ, chỉ một số operation không hoạt động, không có error message rõ ràng.
SMM là gì: mental model đơn giản
Cách dễ hiểu nhất: SMM là một môi trường thực thi hoàn toàn tách biệt với OS, được kích hoạt bởi một tín hiệu đặc biệt gọi là SMI#.
Khi CPU nhận SMI#:
- CPU dừng tất cả những gì đang làm, kể cả OS đang chạy
- Lưu toàn bộ CPU state vào vùng bộ nhớ đặc biệt, gọi là Save State
- Nhảy vào SMRAM, một vùng RAM mà OS không thể đọc/ghi sau khi đã lock đúng cách
- Chạy SMM handler
- Trả về bằng lệnh
RSM, CPU state được khôi phục, OS tiếp tục
OS đang chạy
CPU đang thực thi code bình thường của hệ điều hành hoặc application
SMI xảy ra
Hardware event hoặc software trigger tạo System Management Interrupt
CPU state được lưu
Register state được lưu vào vùng Save State
Vào SMM
CPU chạy SMM Core, Dispatcher và handler trong SMRAM
Return from SMM
CPU khôi phục state, OS tiếp tục như không có gì xảy ra
OS không biết SMM đã chạy. Toàn bộ quá trình này vô hình với OS, trừ khi SMM quá chậm gây ra latency đáng chú ý.
Hai loại SMI phổ biến
Hardware SMI
Được tạo ra bởi chipset khi có event phần cứng: power button, thermal event, GPIO trigger, v.v. Tùy platform và thiết kế board.
Software SMI, SW SMI
Được tạo ra bằng cách ghi vào APM control port, thường là 0xB2:
mov al, 0x42 ; command ID, platform-specific
out 0xB2, al ; trigger SW SMI
Sau lệnh out, chipset tạo SMI#, CPU vào SMM, dispatcher tìm handler đã register cho command 0x42.
// Trong firmware, register SW SMI handler
SmmSwDispatch2->Register (
SmmSwDispatch2,
MySwSmiHandler,
&Context, // chứa SwSmiInputValue = 0x42
&DispatchHandle
);
POST code liên quan SMM
Với AMI Aptio 5.x, SMM init xảy ra trong hai giai đoạn:
| Code | Giai đoạn | Ý nghĩa |
|---|---|---|
| 0x36 | PEI | CPU post-memory initialization, SMM initialization |
| 0x6A | DXE | North Bridge DXE SMM initialization |
| 0x71 | DXE | South Bridge DXE SMM initialization |
Nếu POST code treo tại 0x6A hoặc 0x71, vấn đề liên quan đến SMM initialization trong DXE, thường là SMRAM không được cấp phát đúng, hoặc SMM Core không load được.
Ngoài POST code, một số board in trực tiếp thông báo lỗi SMM ra serial port nếu bật debug build, ví dụ:
SMM Core: SMRAM not found
SMM Core: Failed to allocate SMRAM
SMRAM, vùng bộ nhớ bí ẩn
SMRAM là vùng DRAM mà firmware “giấu” khỏi OS và DXE sau một thời điểm nhất định. Sau khi SMRAM được close/lock đúng cách, chỉ CPU trong SMM mode mới được đọc/ghi vùng này.
Vòng đời của SMRAM:
Reserve SMRAM
SMRAM được phát hiện và reserve, nhưng chưa lock hoàn toàn
Load SMM Core
SMM Core và SMM driver được load vào SMRAM
Register handler
SMM driver register handler trước khi lock
SmmReadyToLock
SMRAM bị close/lock, security policy bắt đầu được áp dụng
Runtime use
SMRAM đã lock, OS không thể access như RAM bình thường
Sau khi SMRAM bị lock, không thể load thêm SMM module mới. Đây là lý do tại sao handler phải được register trước SmmReadyToLock.
Một lỗi cực kỳ phổ biến: developer thêm SMM driver mới nhưng nó dispatch sau SmmReadyToLock vì DEPEX sai hoặc dispatch order sai. Kết quả là handler không được register và service đó không hoạt động, không có error, chỉ im lặng.
SmmReadyToLock: điểm không thể quay lại
Trong EDK II/PI-style firmware, SmmReadyToLock thường được biểu diễn bằng một protocol hoặc notification được signal trong DXE để báo rằng SMM sắp bị khóa. Khi signal này xảy ra:
- SMRAM bị close và lock
- Không thể install thêm SMM module
- Nhiều security policy được áp dụng
DXE execution
SMM driver vẫn có thể dispatch và register handler
Register handler
Handler được register trước lock, OK
Register handler
Handler được register trước lock, OK
SmmReadyToLock
SMRAM close/lock, policy được áp dụng
Dispatch muộn
Quá muộn để register handler, service không hoạt động
Timing bug quanh SmmReadyToLock là một trong những bug khó nhất trong BIOS development vì:
- Không có assert hay error message rõ ràng
- Handler không được register, service không hoạt động im lặng
- Chỉ tái hiện khi một driver nào đó thay đổi dispatch order
SMM Communication: đường nói chuyện an toàn
Khi DXE driver hoặc một firmware component cần yêu cầu service trong SMM, nó thường đi qua SMM Communication Protocol. Sau khi OS đã chạy, nhiều thao tác như SetVariable() đi qua UEFI Runtime Services, rồi bên dưới runtime variable service có thể trigger SMI và chuyển yêu cầu vào SMM. OS không gọi protocol đó trực tiếp như DXE driver.
Firmware component
DXE driver hoặc firmware component tạo CommBuffer ngoài SMRAM
SmmCommunication
Gọi SmmCommunication->Communicate()
Trigger SMI
Firmware path trigger SMI để vào SMM handler
Handler xử lý
Handler validate input, xử lý request, ghi kết quả vào CommBuffer
RSM
DXE driver đọc kết quả sau khi SMM trả về
OS gọi Runtime Service
Ví dụ efibootmgr hoặc OS component gọi SetVariable()
Runtime variable service
Runtime Services nhận request sau ExitBootServices
Trigger SMI
Runtime variable path có thể chuyển request vào SMM
SMM variable handler
Handler áp dụng policy và ghi NVRAM/SPI flash nếu hợp lệ
CommBuffer PHẢI nằm ngoài SMRAM. Đây là validate bắt buộc mà mọi SMM handler cần làm:
// Pseudo code, không phải production code
if (IsAddressInSmram(CommBuffer, CommBufferSize)) {
return EFI_SECURITY_VIOLATION;
}
Nếu bỏ qua validate này, một OS-level attacker có thể tạo CommBuffer trỏ vào SMRAM và đọc/ghi dữ liệu nhạy cảm, đây là class lỗi SMM vulnerability rất nghiêm trọng.
Serial log trong SMM
Debug trong SMM khó hơn DXE vì:
- SMM chạy trong môi trường tách biệt, một số infrastructure bình thường không có sẵn
DEBUG()macro vẫn có thể dùng, nhưng cần config DebugLib phù hợp cho SMM
// Trong SMM driver, sau khi đã setup
DEBUG ((DEBUG_INFO, "SMM Handler: received command 0x%x
", Command));
DEBUG ((DEBUG_INFO, "SMM Handler: CommBuffer at 0x%p, size %d
",
CommBuffer, CommBufferSize));
Để bật serial log trong SMM, cần đảm bảo SMM driver trong INF map đúng DebugLib instance hỗ trợ serial output.
Pattern thường thấy trong log khi debug SMM:
Log từ DXE driver gọi Communicate:
VariableSmm: Communicate called, command = SetVariable
VariableSmm: Waiting for SMM...
VariableSmm: SMM returned EFI_SUCCESS
Log từ SMM handler, nếu có serial log trong SMM:
SmmVar: Handler entered, GUID = ...
SmmVar: SetVariable 'BootOrder', size = 8
SmmVar: Writing to NVRAM...
SmmVar: Done
Nếu thấy DXE gọi Communicate nhưng không thấy log SMM handler, có ba hướng nghi ngờ: handler chưa được register, SMI trigger thất bại, hoặc log level sai.
Debug Diary: Setting lưu không tác dụng
Case này tôi gặp không chỉ một lần. Trên POS system, sau khi thay đổi một số policy như boot timeout, USB policy, network stack và flash firmware mới, nhân viên kỹ thuật của khách hàng vào Setup đổi lại setting, Save & Exit, nhưng sau reboot, mọi thứ về mặc định. Họ đổi lại. Reboot lại. Vẫn thế.
Cảm giác đầu tiên là nghi đâu đó có default-restore logic quá aggressive. Nhưng sau khi bật serial log, thứ xuất hiện là dòng SetVariable() = EFI_WRITE_PROTECTED, im lặng, không pop-up, không báo lỗi gì trên UI cả.
Cách tiếp cận theo layer:
Layer 1: HII/Setup UI có gọi SetVariable không?
Log DXE variable service. Nếu không thấy SetVariable được gọi sau khi user save, vấn đề ở HII callback hoặc Setup Browser.
Layer 2: SetVariable được gọi nhưng có lỗi không?
VariableSmm: SetVariable 'SetupData' = EFI_ACCESS_DENIED
EFI_ACCESS_DENIED ở đây thường có nghĩa: variable attribute yêu cầu authenticated write, hoặc SMM policy đang block write từ DXE/OS.
Layer 3: SetVariable pass nhưng SPI flash không ghi
VariableSmm: SetVariable success
SpiFlash: WriteBlock failed: write protection enabled
Firmware ghi vào NVRAM cache trong RAM thành công, nhưng flush ra SPI flash thất bại do flash write protection chưa được unlock đúng cách.
Layer 4: SPI flash ghi nhưng bị corruption
Ít gặp hơn, nhưng có thể do variable store full hoặc power loss trong quá trình ghi.
Checklist debug: Setting không lưu được
Được và không được làm trong SMM
SMM là môi trường đặc quyền cao, nhưng cũng là môi trường bị giới hạn nhiều hơn DXE. Một số thứ trông “bình thường” ở DXE lại gây crash hoặc behavior không xác định trong SMM.
Được làm
Validate và xử lý communication buffer:
// OK: validate trước khi dùng
if (!IsBufferOutsideMmram(CommBuffer, CommBufferSize)) {
return EFI_SECURITY_VIOLATION;
}
CopyMem(&LocalCopy, CommBuffer, sizeof(MY_REQUEST));
Đọc/ghi hardware register qua MMIO:
// OK: access register phần cứng qua MMIO trong SMM handler
UINT32 Val = MmioRead32(ChipsetBase + REG_OFFSET);
MmioWrite32(ChipsetBase + REG_OFFSET, Val | ENABLE_BIT);
Dùng SMM-safe memory services:
// OK: AllocatePool trong SMM dùng SMRAM pool
VOID *Buf;
Status = SmmAllocatePool(EfiRuntimeServicesData, Size, &Buf);
Đọc CPU Save State để biết context khi SMI xảy ra, register values, instruction pointer, I/O port nếu là I/O trap SMI.
Ghi log qua serial nếu DebugLib đã được config phù hợp cho SMM.
Không được làm, và hậu quả
1. Gọi Boot Services sau khi chúng đã bị vô hiệu hóa
Sau ExitBootServices(), tất cả Boot Services không còn hợp lệ. Nếu SMM handler vẫn cố gọi gBS->AllocatePool() hoặc bất kỳ Boot Service nào:
Undefined behavior, thường dẫn đến CPU exception hoặc system hang
Lý do: gBS pointer vẫn tồn tại trong memory, nhưng code mà nó trỏ tới đã bị giải phóng hoặc không còn valid. CPU sẽ jump vào vùng garbage.
2. Gọi DXE protocol từ SMM handler
SMM handler không được locate và dùng DXE protocol trực tiếp. SMM chạy trong môi trường tách biệt, DXE handle database không accessible từ đây theo cách thông thường.
Có thể dẫn đến page fault hoặc exception vì truy cập memory không được phép trong SMM address space
3. Truy cập SMRAM từ ngoài SMM mode
Khi SMRAM đã lock, mọi read/write từ OS hay DXE driver vào vùng SMRAM sẽ bị hardware từ chối. Tùy platform, access có thể bị chặn, trả dữ liệu không hợp lệ, bị ignore, hoặc gây lỗi phần cứng. Không nên dựa vào một behavior cố định.
Đây là cố ý. Hardware đảm bảo OS không thể spy hay patch SMM.
4. Trigger SMI từ SMM handler, nested SMI
Nested SMI là vùng rất platform-dependent. Nhiều chipset sẽ mask hoặc defer SMI khi CPU đang ở SMM. Không nên thiết kế handler phụ thuộc vào việc trigger SMI lồng nhau.
5. Dùng floating point hoặc SIMD mà không save/restore state
CPU Save State khi vào SMM mặc định không lưu FPU/SSE/AVX registers. Nếu handler dùng float hoặc SIMD mà không tự save/restore:
OS/application đang chạy mất dữ liệu FPU register
Crash ở tầng OS, không phải tầng firmware
Bug cực kỳ khó trace vì không có error trong SMM
6. Thực hiện long operation làm block CPU quá lâu
SMM block toàn bộ CPU và các CPU khác trong hệ thống đa nhân trong khi chạy. Nếu handler làm gì đó mất nhiều millisecond, ví dụ đợi flash write mà không có timeout:
OS scheduler bị stall, watchdog có thể timeout
System hang hoặc NMI
Trên laptop: battery/thermal management bị gián đoạn
CPU Exception trong SMM, nhận biết và trace
Khi code trong SMM gây CPU exception, ví dụ page fault hoặc general protection fault, hành vi khác với exception trong OS.
Không có OS exception handler. SMM chạy trong môi trường riêng, không có kernel exception handler để catch lỗi và in stack trace.
Kết quả thường thấy:
System hang hoàn toàn, không có log thêm
hoặc
System reset tự động, POST lại từ đầu
hoặc trên một số platform với EFI_SMM_CPU_PROTOCOL:
SMM exception handler trong firmware log lỗi vào serial trước khi reset
Cách trace:
Nếu bạn thấy system bị reset unexpectedly sau một SW SMI hoặc sau một operation đi qua SMM, đây là hướng kiểm tra:
Trace CPU exception trong SMM
Tóm tắt, những điểm không được quên với SMM
| Dấu hiệu / chủ đề | Điểm cần nhớ | Hướng kiểm tra |
|---|---|---|
| Handler register timing | Phải trước SmmReadyToLock | Sau lock handler không được register, service im lặng không chạy |
| CommBuffer location | Phải ngoài SMRAM | Thiếu validate có thể thành SMM vulnerability |
| POST 0x6A / 0x71 treo | SMM init DXE fail | Kiểm tra SMRAM allocation, SMM Core |
| POST 0x36 treo | PEI-phase SMM init fail | Liên quan CPU/chipset SMM config |
| SetVariable = EFI_ACCESS_DENIED | Policy hoặc protection block | Variable attribute, auth policy, SMM policy, flash write protection |
| Setting quay về cũ sau reboot | Không chỉ lỗi UI | Debug từ HII → SetVariable → SMM → SPI flash |
Tài liệu tham khảo
- AMI Aptio 5.x Status Codes, Public Document, POST code range cho SMM init
- EDK II PiSmmCore, GitHub, SMM Core source, public reference
- EDK II SMM modules, GitHub, SMM infrastructure modules
- UEFI PI Specification, Volume 4: SMM spec chính thức
- EDK II Debugging, TianoCore Wiki, debug config cho SMM driver
- Lauterbach UEFI H2O Awareness Manual, debug tool cho Insyde H2O, có phần SMM debug
Đọc tiếp
- SMM Architecture Overview
- SMRAM là gì?
- SW SMI là gì?
- SMM Handler là gì?
- SMM Communication là gì?
- SmmReadyToLock là gì?
- DXE Phase: Driver, Protocol và cách đọc log (bài liên quan)
Thấy nội dung này hữu ích?
Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
SMM là gì?
SMM là chế độ CPU đặc biệt để firmware xử lý tác vụ nhạy cảm ngoài tầm kiểm soát OS. Hiểu SMM giúp debug variable write treo, flash fail và security issue.
SMRAM là gì?
Giải thích SMRAM như vùng memory bảo vệ cho SMM: layout, open/closed/locked state, Save State và checklist debug protection.
SMI là gì?
SMI là interrupt đưa CPU vào SMM để chạy handler. Có nhiều nguồn SMI: SW, GPIO, timer, chipset. Hiểu SMI giúp debug latency spike và SMM communication.
Đọc thêm về BIOS/UEFI
Khám phá các bài viết về BIOS/UEFI, embedded firmware, debugging và system-level thinking.