UEFI Boot Flow: từ Reset Vector đến ExitBootServices
Không phải học để thuộc tên phase. Học boot flow để biết máy đang chết ở đâu, và debug theo hướng nào: từ SEC đến Runtime.
Có những lỗi nhìn từ bên ngoài rất giống nhau: máy bật nguồn nhưng màn hình đen, logo vendor hiện lên rồi reset, SSD thấy trong setup nhưng không có trong boot menu, Windows Boot Manager có đó nhưng bấm vào lại quay về màn hình boot.
Với người dùng, tất cả chỉ là “máy không boot”.
Nhưng với firmware engineer, những lỗi đó nằm ở những lớp hoàn toàn khác nhau. Có lỗi chết từ rất sớm, khi DRAM còn chưa được khởi tạo. Có lỗi xảy ra lúc firmware đang load driver. Có lỗi chỉ xuất hiện khi BDS đọc Boot#### trong NVRAM. Có lỗi lại nằm ở ranh giới cuối cùng, khi OS loader gọi ExitBootServices.
Đây là lý do học UEFI Boot Flow không phải để thuộc lòng tên phase. SEC, PEI, DXE, BDS, TSL hay Runtime chỉ có ý nghĩa khi bạn biết mỗi phase đang có tài nguyên gì, đang dựng phần nào của hệ thống, và lỗi ở đó thì debug theo hướng nào.
Công việc của mình là custom BIOS cho hệ thống POS, kiosk và terminal thanh toán. Boot fail ở môi trường đó không phải kiểu “để từ từ xem”. Nếu máy chết ở cửa hàng lúc giờ cao điểm, điều quan trọng nhất là xác định thật nhanh: firmware đang chết ở phase nào?
Bài này sẽ đi từ Reset Vector đến ExitBootServices theo đúng góc nhìn đó. Không phải học boot flow như một sơ đồ để nhớ, mà học như một bản đồ debug.
Vì sao cần chia phase?
Vấn đề căn bản: khi CPU vừa reset, hệ thống gần như không có gì để làm việc. DRAM chưa được khởi tạo, chipset chưa được cấu hình, không có stack bình thường, không có heap. Firmware không thể làm tất cả mọi thứ ngay từ đầu.
UEFI giải quyết bằng cách chia boot thành các phase, mỗi phase dựng nền cho phase sau:
Security Phase
CPU reset. Tài nguyên gần như zero. Dựng temporary RAM để PEI chạy được
Pre-EFI Init
Khởi tạo DRAM. Dựng HOB list. Bàn giao dữ liệu platform cho DXE
Driver Execution
Load driver. Publish protocol. Enumerate device. Dựng toàn bộ hệ sinh thái firmware
Boot Device Select
Đọc BootOrder và Boot####. Resolve Device Path. Load OS loader
Transient System Load
OS loader đang chạy. Chuẩn bị ExitBootServices
Runtime Phase
OS tiếp quản. Chỉ còn Runtime Services: variable, time, reset
Điều quan trọng để nhớ: lỗi ở mỗi phase biểu hiện khác nhau. Không thể debug BDS nếu vấn đề thật sự nằm ở PEI. Và không thể debug DXE driver nếu root cause là HOB sai từ PEI.
SEC: khi hệ thống chưa có gì
Ngay sau khi CPU reset, hệ thống ở trạng thái cực kỳ giới hạn: DRAM chưa được khởi tạo, stack bình thường chưa tồn tại. Firmware phải bắt đầu chạy trong điều kiện này.
SEC làm đúng một việc: thiết lập môi trường tối thiểu để PEI có thể chạy.
Reset vector
↓
Thiết lập Temporary RAM (cache-as-RAM hoặc internal SRAM)
↓
Xác minh firmware sơ bộ nếu platform yêu cầu
↓
Chuyển quyền sang PEI Core
SEC không làm nhiều chính sách. Nó là đoạn code “dọn đường” để PEI có chỗ đứng.
Khi nào nghi SEC?
Nếu board chết rất sớm, không có POST code, không có serial log, reset loop ngay từ đầu. Lúc này chỉ có POST code LED hoặc hardware debug port mới cho thông tin, vì các công cụ thông thường chưa có.
PEI: dựng nền tảng cho DXE
PEI là phase dựng hạ tầng tối thiểu để DXE có thể chạy. Nhiệm vụ quan trọng nhất: khởi tạo DRAM. Khi RAM chưa sẵn sàng, firmware không thể load driver, không thể tạo service, không thể làm gì nhiều hơn mức tối thiểu.
Ngoài memory init, PEI còn tạo HOB (Hand-Off Block), các khối dữ liệu mô tả memory map, firmware volume, boot mode và thông tin platform để bàn giao cho DXE:
PEI tạo HOB list:
├─ Memory Resource HOB: DXE biết RAM ở đâu, bao nhiêu
├─ Firmware Volume HOB: DXE dispatcher biết tìm driver ở đâu
├─ GUID HOB (platform-specific): thông tin board, silicon, policy
└─ Boot Mode: Normal / S3 Resume / Recovery
HOB là cầu nối giữa PEI và DXE. DXE không biết gì về hardware, trừ những gì PEI đã ghi vào HOB. Nếu HOB thiếu hoặc sai, DXE có thể vẫn khởi động nhưng với dữ liệu nền tảng không đúng.
Khi nào nghi PEI?
- POST code dừng ở PEI range
- Reset loop sau memory training
- DXE bắt đầu chạy nhưng thiếu driver hoặc resource. Đừng vội sửa DXE, kiểm tra HOB list trước.
DXE: hệ sinh thái driver và service
Sau khi memory sẵn sàng và HOB có đủ dữ liệu, DXE Core bắt đầu dựng “hệ sinh thái” của UEFI: load driver, tạo handle database, publish protocol, enumerate device.
DXE Dispatcher scan firmware volume, kiểm tra DEPEX (dependency expression) của từng driver, và chỉ dispatch driver khi tất cả protocol nó cần đã được install. Điều này có nghĩa là thứ tự dispatch phụ thuộc vào dependency chain, không phải vào vị trí trong FV.
DXE Dispatcher loop:
Scan FV → kiểm tra DEPEX
Protocol chưa có: driver chờ
Protocol đã có: dispatch → driver install protocol mới → unlock driver đang chờ
Lặp lại đến khi không còn gì để dispatch → chuyển sang BDS
Khi nào nghi DXE?
Device không lên boot menu không có nghĩa là BDS sai. Hãy kiểm tra theo thứ tự:
Checklist debug DXE
BDS: từ firmware sang OS loader
BDS đọc boot option từ NVRAM, connect device cần thiết, parse Device Path, load file .efi và chuyển quyền cho OS loader.
Mỗi boot option lưu trong NVRAM dưới dạng biến Boot####:
BootOrder: 0000, 0001, 0002
Boot0000: Windows Boot Manager
├─ Device Path: HD(1,GPT,...)/File(\EFI\Microsoft\Boot\bootmgfw.efi)
└─ Attributes: LOAD_OPTION_ACTIVE
Boot0001: UEFI Shell
Boot0002: PXE Boot
BDS đọc BootOrder, thử từng option. Với mỗi option, nó resolve Device Path (connect controller, tìm ESP), load file .efi, rồi gọi StartImage().
Khi nào nghi BDS?
SSD được DXE nhận nhưng Windows không boot. Trước khi nghi Windows Boot Manager, kiểm tra:
Checklist debug BDS
TSL: vùng chuyển tiếp trước OS
TSL là khoảng thời gian OS loader đã được BDS load và đang chạy, nhưng OS vẫn chưa hoàn toàn tiếp quản. Bootloader đang dùng UEFI services để đọc kernel, chuẩn bị memory, rồi gọi ExitBootServices().
ExitBootServices() là điểm không thể quay lại. Sau lời gọi này, Boot Services không còn hợp lệ và chỉ còn Runtime Services.
Lỗi TSL là một trong những lỗi khó debug nhất vì xảy ra ở ranh giới firmware và OS:
// Bootloader phải lấy memory map và gọi ExitBootServices
// với đúng MapKey. Nếu memory map thay đổi giữa hai bước: fail.
GetMemoryMap(&MapSize, MemMap, &MapKey, ...);
// Đừng allocate/free memory ở đây
ExitBootServices(ImageHandle, MapKey); // MapKey phải khớp
Khi nào nghi TSL?
- OS loader đã load xong nhưng kernel không lên
ExitBootServices()trảEFI_INVALID_PARAMETER- Lỗi xảy ra sau
StartImage()thành công
Runtime: sau khi OS tiếp quản
Sau ExitBootServices(), OS kernel chạy. UEFI Boot Services không còn, nhưng Runtime Services vẫn có thể được gọi từ OS nếu được map đúng: đọc/ghi NVRAM variable, reset hệ thống, lấy thời gian.
Trên nhiều platform, đường đi thực tế của SetVariable là:
OS gọi SetVariable("BootOrder", ...)
↓
Runtime Variable Service
↓ (nhiều platform)
SMM Variable Driver
↓
SPI flash write
Với lỗi variable sau khi OS đã chạy, đặc biệt là BootOrder hoặc efibootmgr ghi không có tác dụng, cần kiểm tra Runtime variable path, SMM variable driver và SPI flash. Còn với setting đổi từ Setup rồi reboot mất, vẫn phải kiểm tra cả HII save flow, NVRAM và module consumer.
Debug theo triệu chứng
Khi gặp sự cố boot, câu hỏi đầu tiên không phải là “lỗi gì” mà là “chết ở phase nào”:
| Triệu chứng | Phase nên nghi ngờ | Nên kiểm tra gì |
|---|---|---|
| Không có log, reset loop sớm | SEC | DRAM chưa init, chỉ có POST code LED hoặc hardware trace. |
| Treo ở PEI POST code, reset sau memory training | PEI | Memory init, HOB, PEI → DXE handoff. |
| Device không xuất hiện trong boot menu | DXE | Driver dispatch, DEPEX, Driver Binding, protocol chain. |
| Boot menu có nhưng OS không boot | BDS | BootOrder, Boot####, Device Path, LoadImage. |
| OS loader chạy nhưng kernel không lên | TSL | ExitBootServices, memory map key, loader fail. |
| Setting lưu không tác dụng sau reboot | Runtime / HII / NVRAM | Tùy setting được đổi từ OS hay từ Setup UI. Kiểm tra cả Runtime path và HII save flow. |
Tóm tắt
UEFI boot flow không phải chạy từ đầu đến cuối như một hàm main(). Nó là chuỗi phase, mỗi phase có môi trường riêng, nhiệm vụ riêng và cách fail riêng.
Temporary RAM
CPU reset, dựng môi trường cực sớm, gọi PEI
Memory init
Khởi tạo DRAM, tạo HOB, bàn giao cho DXE
Driver model
Dispatcher, protocol, handle database, driver binding
Boot option
BootOrder, Boot####, Device Path, LoadImage, StartImage
OS loader
GetMemoryMap, ExitBootServices
Runtime Services
NVRAM variable, time, reset, SMM path tùy platform
Khi debug, việc đầu tiên không phải là đọc source code chi tiết, mà là xác định chính xác firmware đang chết ở đâu trong chuỗi này. Khi biết phase, bạn biết lớp nào cần nhìn và lớp nào có thể bỏ qua.
Tài liệu tham khảo
Đọc tiếp
Mỗi phase trong bài này có note chuyên sâu hơn:
- SEC là gì?
- PEI là gì?
- PEI → DXE Handoff là gì?
- DXE là gì?
- DXE Dispatcher là gì?
- Driver Binding Flow là gì?
- BDS là gì?
- BDS → TSL Handoff là gì?
- TSL là gì?
- ExitBootServices là gì?
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.
Firmware Execution Flow Overview
Knowledge map về luồng thực thi firmware từ SEC, PEI, DXE, BDS tới TSL và Runtime.
DXE Phase: Driver, Protocol và cách đọc log khi mọi thứ không chạy
Deep dive vào DXE phase: dispatcher, DEPEX, Driver Binding, protocol database, kèm cách dùng serial log, POST code và UEFI Shell để debug thực tế.
BIOS và UEFI khác nhau như thế nào?
Vì sao ai cũng nói 'vào BIOS' dù máy hiện nay dùng UEFI? Bài viết giải thích BIOS, UEFI, boot flow, Secure Boot, Boot#### và cách firmware dựng hệ thống trước khi OS chạy.
Đọ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.