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.

Cập nhật 14 phút đọc
BIOS / UEFI cover

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_DENIED hoặc EFI_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#:

  1. CPU dừng tất cả những gì đang làm, kể cả OS đang chạy
  2. Lưu toàn bộ CPU state vào vùng bộ nhớ đặc biệt, gọi là Save State
  3. Nhảy vào SMRAM, một vùng RAM mà OS không thể đọc/ghi sau khi đã lock đúng cách
  4. Chạy SMM handler
  5. Trả về bằng lệnh RSM, CPU state được khôi phục, OS tiếp tục
01 OS

OS đang chạy

CPU đang thực thi code bình thường của hệ điều hành hoặc application

02 SMI#

SMI xảy ra

Hardware event hoặc software trigger tạo System Management Interrupt

03 Save State

CPU state được lưu

Register state được lưu vào vùng Save State

04 SMRAM

Vào SMM

CPU chạy SMM Core, Dispatcher và handler trong SMRAM

05 RSM

Return from SMM

CPU khôi phục state, OS tiếp tục như không có gì xảy ra

Mental model SMM: SMI đưa CPU vào một môi trường thực thi tách biệt, sau đó RSM trả CPU về luồng cũ

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:

POST code liên quan SMM init
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:

01 PEI

Reserve SMRAM

SMRAM được phát hiện và reserve, nhưng chưa lock hoàn toàn

02 DXE

Load SMM Core

SMM Core và SMM driver được load vào SMRAM

03 Register

Register handler

SMM driver register handler trước khi lock

04 Lock

SmmReadyToLock

SMRAM bị close/lock, security policy bắt đầu được áp dụng

05 BDS/OS

Runtime use

SMRAM đã lock, OS không thể access như RAM bình thường

Vòng đời SMRAM: handler phải được register trước khi SMRAM bị lock

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:

  1. SMRAM bị close và lock
  2. Không thể install thêm SMM module
  3. Nhiều security policy được áp dụng
01 DXE

DXE execution

SMM driver vẫn có thể dispatch và register handler

02 Driver A

Register handler

Handler được register trước lock, OK

03 Driver B

Register handler

Handler được register trước lock, OK

04 Lock

SmmReadyToLock

SMRAM close/lock, policy được áp dụng

05 Driver C

Dispatch muộn

Quá muộn để register handler, service không hoạt động

Timing bug quanh SmmReadyToLock: driver dispatch muộn có thể không register được handler

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.

01 DXE

Firmware component

DXE driver hoặc firmware component tạo CommBuffer ngoài SMRAM

02 Communicate

SmmCommunication

Gọi SmmCommunication->Communicate()

03 SMI

Trigger SMI

Firmware path trigger SMI để vào SMM handler

04 SMM

Handler xử lý

Handler validate input, xử lý request, ghi kết quả vào CommBuffer

05 Return

RSM

DXE driver đọc kết quả sau khi SMM trả về

Boot-time/DXE path: firmware component nói chuyện với SMM qua SMM Communication Protocol
01 OS

OS gọi Runtime Service

Ví dụ efibootmgr hoặc OS component gọi SetVariable()

02 RT

Runtime variable service

Runtime Services nhận request sau ExitBootServices

03 SMI

Trigger SMI

Runtime variable path có thể chuyển request vào SMM

04 SMM

SMM variable handler

Handler áp dụng policy và ghi NVRAM/SPI flash nếu hợp lệ

OS/runtime path: OS không gọi SmmCommunication Protocol trực tiếp, mà đi qua Runtime Services

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ì:

  1. 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
  2. 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

Debug SMM: những điểm không được quên
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

Đọc tiếp

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.

Đọ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.