BIOS Setup là gì: từ màn hình xanh cổ điển đến HII và NVRAM hiện đại

BIOS Setup hiện đại được xây dựng trên HII: VFR, VarStore, NVRAM. Tại sao thêm field vào giữa struct phá hỏng hệ thống và cách debug setting không lưu được.

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

“Vào BIOS” thật ra là vào firmware setup

Nếu bạn lớn lên với máy tính từ những năm 2000 trở về trước, hẳn còn nhớ hình ảnh đó: nhấn Del hoặc F2 khi mới bật máy, và xuất hiện một màn hình xanh hoặc xám với chữ trắng, dùng phím mũi tên để điều hướng, Enter để chọn, +/- để thay đổi giá trị. Không có chuột, không có đồ họa, không có giao diện hiện đại.

Đó là giao diện của Legacy BIOS Setup, AMI BIOS, Award BIOS, Phoenix BIOS. Nó hoạt động tốt cho thời đó, nhưng bị giới hạn nặng nề về giao diện, đa ngôn ngữ, và khả năng mở rộng.

Ngày nay, khi vào “BIOS” của một laptop hiện đại hay server, bạn thấy gì? Giao diện đồ họa đầy màu sắc, có chuột, có search box, thậm chí có cả logo hãng. Hỗ trợ nhiều ngôn ngữ. Tổ chức theo tab và submenu có cấu trúc.

Trong thực tế, nhiều người vẫn gọi đó là “vào BIOS”, và không sai hoàn toàn, vì về mặt chức năng nó làm cùng một việc. Nhưng bên trong, nó được xây dựng bằng một kiến trúc hoàn toàn khác tên là HII, Human Interface Infrastructure.

Cách vào Setup, và cái tên “BIOS” vẫn còn đó

Cách phổ biến nhất để vào UEFI Setup:

  • Del hoặc F2, chuẩn nhất, dùng trên hầu hết PC và laptop
  • F10, thường gặp trên HP
  • F1, IBM/Lenovo ThinkPad truyền thống
  • Esc, một số laptop Asus hoặc Sony

Trên một số hệ thống hiện đại, cửa sổ boot quá nhanh để kịp nhấn phím. Cách vào từ Windows: Settings → Recovery → Advanced startup → Restart now → Troubleshoot → UEFI Firmware Settings.

HII: kiến trúc đằng sau cái UI bóng bẩy đó

Nếu Legacy BIOS Setup là một đoạn code C monolithic vẽ trực tiếp lên màn hình, thì UEFI HII là một hệ thống pipeline có cấu trúc, tách biệt rõ ràng giữa dữ liệu, giao diện, và storage.

HII (Human Interface Infrastructure) bao gồm:

VFR source    → mô tả form, question, điều kiện
.UNI file     → string đa ngôn ngữ (Tiếng Anh, Tiếng Việt, Tiếng Nhật...)
Build tool    → compile VFR → IFR binary, UNI → String Package
DXE driver    → install package vào HII Database
Setup Browser → đọc IFR, render UI, xử lý input
VarStore      → backing storage của từng question
Config Access → callback driver xử lý save/validate/notify
NVRAM         → nơi dữ liệu cuối cùng được lưu persistent

Cái hay của kiến trúc này: mỗi DXE driver có thể tự đưa thêm form vào Setup mà không cần sửa code Browser. Browser chỉ đọc HII Database và render, nó không cần biết có bao nhiêu driver đang cung cấp form.

Đây cũng là lý do Setup menu của một server có thể có hàng trăm option được tổ chức phức tạp, trong khi codebase vẫn có cấu trúc rõ ràng.

VFR: source code của Setup UI

Developer không viết code vẽ màn hình. Họ viết VFR, Visual Forms Representation, một ngôn ngữ mô tả form, question và điều kiện hiển thị.

varstore SETUP_DATA,
  varid = Setup,
  name  = Setup,
  guid  = SETUP_GUID;

form formid = FORM_BOOT_MAIN,
  title = STRING_TOKEN(STR_BOOT_FORM);

  oneof varid   = Setup.SataMode,
        prompt  = STRING_TOKEN(STR_SATA_MODE),
        help    = STRING_TOKEN(STR_SATA_MODE_HELP),
        questionid = QUESTION_ID_SATA_MODE;
    option text = STRING_TOKEN(STR_AHCI),  value = 0, flags = DEFAULT;
    option text = STRING_TOKEN(STR_RAID),  value = 1;
  endoneof;

  checkbox varid   = Setup.FastBoot,
           prompt  = STRING_TOKEN(STR_FAST_BOOT),
           help    = STRING_TOKEN(STR_FAST_BOOT_HELP),
           questionid = QUESTION_ID_FAST_BOOT;
  endcheckbox;
endform;

VFR được compile thành IFR (Internal Forms Representation), một dạng binary bytecode. Setup Browser đọc IFR, không đọc VFR source. Điều này có một ý nghĩa quan trọng cho debug: nếu có IFR Extractor, bạn có thể đọc ngược ra structure của Setup form từ một BIOS image mà không cần source code.

VarStore và struct: nơi dữ liệu thật sự sống

Mỗi question trong VFR cần một VarStore, backing storage để đọc và ghi giá trị.

Kiểu phổ biến nhất là Buffer VarStore, map trực tiếp vào một C struct:

// Định nghĩa trong header
typedef struct {
    UINT8  FastBoot;        // offset 0x00
    UINT8  SataMode;        // offset 0x01
    UINT8  NetworkStack;    // offset 0x02
    UINT8  Reserved;        // offset 0x03, padding để align
    UINT16 BootTimeout;     // offset 0x04
    UINT8  TpmDevice;       // offset 0x06
    UINT8  SecureBootUi;    // offset 0x07
} SETUP_DATA;

Khi user đổi “SATA Mode” sang RAID và nhấn Save, firmware ghi giá trị 1 vào offset 0x01 của buffer này. Sau reboot, PEI hoặc DXE driver đọc lại buffer, đọc Setup.SataMode, và cấu hình storage controller tương ứng.

Vấn đề offset: tại sao một sự thay đổi nhỏ có thể phá hỏng hệ thống

Đây là loại bug tôi từng làm mà mất khá lâu để hiểu tại sao. Lúc đó đang thêm một feature mới cho POS firmware, một flag nhỏ thôi, UINT8, thêm vào giữa struct cho “gọn”. Build xong, flash vào máy test, boot lên, máy không thấy ổ cứng. Serial log không có gì bất thường. Kiểm tra cable, kiểm tra SATA controller, kiểm tra DXE driver, đều ổn.

Mất gần nửa buổi mới nghĩ đến việc dump NVRAM và so sánh offset từng byte với struct mới. Lệch một byte. SataMode đang đọc giá trị của field bên cạnh.

Hãy tưởng tượng bạn cần thêm một field mới vào SETUP_DATA. Bạn thêm vào giữa struct:

// Trước khi thêm
typedef struct {
    UINT8  FastBoot;        // offset 0x00
    UINT8  SataMode;        // offset 0x01
    UINT8  NetworkStack;    // offset 0x02
    UINT8  Reserved;        // offset 0x03
    UINT16 BootTimeout;     // offset 0x04
} SETUP_DATA;

// Sau khi thêm field mới ở giữa
typedef struct {
    UINT8  FastBoot;        // offset 0x00
    UINT8  NewFeature;      // offset 0x01, thêm vào đây
    UINT8  SataMode;        // offset 0x02, bị đẩy xuống
    UINT8  NetworkStack;    // offset 0x03
    UINT8  Reserved;        // offset 0x04
    UINT16 BootTimeout;     // offset 0x05, bị đẩy xuống
} SETUP_DATA;

Struct C mới đã thay đổi. Nhưng NVRAM vẫn giữ dữ liệu theo layout cũ. Khi firmware mới boot lên và đọc Setup.SataMode tại offset 0x02, nó thật ra đang đọc giá trị của NetworkStack trong layout cũ.

Kết quả có thể là: SATA mode bị set sai, storage controller không nhận ra ổ cứng, máy không boot được. Hoặc tệ hơn, một field security-related như TPM bị đọc sai giá trị, kéo theo hàng loạt hệ quả khó debug.

Đây cũng là lý do tại sao khi nhận một project BIOS mới để maintain, việc đầu tiên nhiều engineer làm là dump toàn bộ NVRAM variable và so sánh offset với struct trong source code. Lệch một byte là sai hết.

Flow hoàn chỉnh: từ user click đến boot phase dùng setting

Khi user vào Setup, thay đổi một option và nhấn Save, đây là những gì thật sự xảy ra:

01 User thay đổi

Browser lưu tạm

Giá trị mới được giữ trong browser storage tạm, chưa ghi NVRAM. Callback được gọi nếu driver đăng ký

02 Save & Exit

RouteConfig() được gọi

Browser gọi RouteConfig() trên driver sở hữu FormSet. Đây mới là bước thật sự ghi dữ liệu

03 SetVariable

Ghi vào NVRAM

Driver gọi SetVariable. Nếu fail ở đây, UI hiện đúng nhưng reboot sẽ mất setting

04 Reset

System reboot

Máy khởi động lại với dữ liệu mới trong SPI flash

05 Consumer

PEI/DXE đọc setting

GetVariable → Setup.SataMode == 1 → controller được config theo RAID mode

Flow save BIOS setting: Browser tạm → RouteConfig → SetVariable → NVRAM → consumer

Bước quan trọng nhất và dễ bị bỏ qua: trong nhiều implementation, Setup Browser giữ giá trị trong browser storage tạm. Chỉ khi Save & Exit, RouteConfig() mới được gọi và Config Access driver mới ghi xuống variable store thật sự.

Nếu SetVariable() trả lỗi (ví dụ: EFI_WRITE_PROTECTED khi flash protection đã bật), UI vẫn hiển thị giá trị mới nhưng NVRAM vẫn là giá trị cũ. Reset xong mọi thứ quay về như cũ.

Debug: option lưu không được giữ sau reboot

Case phổ biến nhất khi làm BIOS. Cách tiếp cận:

Debug setting không được giữ sau reboot
Bước Cần xác nhận Ý nghĩa khi fail
1 RouteConfig có chạy không? Nếu không chạy, lỗi có thể nằm ở FormSet GUID hoặc Config Access Protocol registration
2 SetVariable có thành công không? Nếu fail, kiểm tra status như EFI_WRITE_PROTECTED hoặc EFI_ACCESS_DENIED
3 NVRAM sau reboot có đúng không? Nếu lệch, có thể struct layout thay đổi nhưng chưa migrate
4 Consumer đọc đúng field không? PEI/DXE module có thể đọc sai GUID, name hoặc offset
5 Default restore path có overwrite không? Clear CMOS hoặc Load Default có thể ghi đè variable

Tooling và debug

Các vendor như AMI và Insyde thường có tool riêng để inspect setup form, variable, IFR và BIOS image. AMI Debug Rx, InsydeH2O variable editor, Lauterbach UEFI awareness: những tool này hữu ích, đặc biệt khi không có source code đầy đủ.

Nhưng dù dùng tool nào, người debug vẫn phải hiểu pipeline HII và layout VarStore. Một AI có thể gợi ý “offset này có thể bị lệch”, nhưng để hiểu tại sao lệch là nguy hiểm và làm thế nào sửa đúng mà không phá upgrade path từ version cũ, vẫn cần nền tảng kỹ thuật.

Tóm tắt

  • “Vào BIOS” ngày nay thực ra là vào UEFI Setup: kiến trúc HII, không phải Legacy BIOS
  • HII tách biệt UI (VFR/IFR), storage (VarStore/NVRAM), và logic (Config Access/callback)
  • VarStore offset là điểm cực kỳ nhạy cảm, thêm field vào giữa struct là phá hỏng NVRAM cũ
  • Flow save đi qua Browser → RouteConfig → SetVariable → NVRAM → Policy consumer
  • Debug nên theo đúng tầng: hiển thị → lưu trữ → consumer

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.