ExitBootServices là gì?

ExitBootServices là ranh giới chuyển giao từ firmware sang OS. Sau lời gọi này Boot Services bị tắt, OS nhận quyền kiểm soát memory và platform.

Cập nhật 5 phút đọc
BIOS Terms cover

ExitBootServices() là điểm chuyển giao lớn giữa firmware boot-time environment và OS. Sau lời gọi này, Boot Services bị tắt hoàn toàn, OS nhận quyền kiểm soát memory và platform ở mức cao hơn.

Đây là ranh giới không thể quay lại. Sau ExitBootServices(), firmware không thể gọi lại Boot Services, không thể install/locate protocol, và không thể allocate boot-time memory nữa.

Flow chuẩn của OS loader

01

GetMemoryMap()

Lấy memory map và MapKey hiện tại. Sau lần GetMemoryMap cuối cùng, tránh allocate thêm.

02

ExitBootServices()

Gọi với MapKey vừa lấy. Nếu fail với EFI_INVALID_PARAMETER, quay lại bước 01.

03

SetVirtualAddressMap()

Nếu OS dùng virtual mapping cho Runtime Services, báo địa chỉ virtual cho firmware runtime code.

04

OS runtime

Boot Services đã tắt. Chỉ còn Runtime Services qua gRT.

MapKey - nguồn gốc của lỗi phổ biến nhất

GetMemoryMap() trả về cùng lúc memory map và một MapKey - token đại diện cho trạng thái memory tại thời điểm đó. ExitBootServices() nhận MapKey này để verify memory map chưa thay đổi.

Nếu bất kỳ AllocatePool, FreePool, AllocatePages, hay FreePages nào xảy ra giữa GetMemoryMap()ExitBootServices(), firmware sẽ tạo MapKey mới. Gọi ExitBootServices() với key cũ sẽ trả EFI_INVALID_PARAMETER.

Flow retry chuẩn (pseudo-code rút gọn - OS loader thật cần xử lý buffer/free/error kỹ hơn):

EFI_STATUS      Status;
UINTN           MemoryMapSize = 0;
EFI_MEMORY_DESCRIPTOR *MemoryMap = NULL;
UINTN           MapKey;
UINTN           DescriptorSize;
UINT32          DescriptorVersion;

do {
  // Lấy memory map - có thể cần gọi 2 lần để lấy đúng size
  Status = gBS->GetMemoryMap(
                  &MemoryMapSize,
                  MemoryMap,
                  &MapKey,
                  &DescriptorSize,
                  &DescriptorVersion
                  );
  if (Status == EFI_BUFFER_TOO_SMALL) {
    // Allocate buffer - lưu ý: AllocatePool làm MapKey thay đổi
    // Nên GetMemoryMap lại sau khi allocate
    MemoryMapSize += DescriptorSize * 4; // thêm buffer dự phòng
    gBS->AllocatePool(EfiLoaderData, MemoryMapSize, (VOID **)&MemoryMap);
    continue;
  }

  // Gọi ExitBootServices - nếu fail vì MapKey stale thì loop lại
  Status = gBS->ExitBootServices(ImageHandle, MapKey);

} while (Status == EFI_INVALID_PARAMETER);

Sau lần GetMemoryMap() cuối cùng thành công, OS loader nên gọi ExitBootServices() càng trực tiếp càng tốt - tránh mọi thao tác có thể làm thay đổi memory map.

Điều gì xảy ra khi ExitBootServices() được gọi?

Ở mức khái niệm, firmware sẽ kết thúc boot-time environment:

1. Signal EVT_SIGNAL_EXIT_BOOT_SERVICES
   → Runtime driver có cơ hội cleanup dependency vào Boot Services

2. Kết thúc Boot Services infrastructure
   → Sau khi ExitBootServices() thành công, gBS->... không còn hợp lệ

3. Chuyển quyền kiểm soát boot-time memory cho OS
   → OS có thể reclaim EfiBootServicesCode/Data

EVT_SIGNAL_EXIT_BOOT_SERVICES là event đặc biệt mà runtime driver có thể đăng ký nếu cần dọn dẹp dependency vào Boot Services trước khi Boot Services bị tắt:

// Runtime driver đăng ký trong entry point
EFI_EVENT ExitBootServicesEvent;

gBS->CreateEventEx(
       EVT_NOTIFY_SIGNAL,
       TPL_NOTIFY,
       MyExitBootServicesHandler,
       NULL,
       &gEfiEventExitBootServicesGuid,
       &ExitBootServicesEvent
       );

// Handler chạy khi ExitBootServices() được gọi
VOID EFIAPI
MyExitBootServicesHandler (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  // Cleanup mọi thứ phụ thuộc vào Boot Services
  // Tắt timer, close event, release boot-time resource
  // Handler nên chạy ngắn, tránh allocate/wait/gọi Boot Services phức tạp.
  // Sau khi ExitBootServices() hoàn tất, không được gọi gBS nữa.
}

Trách nhiệm sau ExitBootServices

Mục Giá trị Ghi chú
Firmware runtime driver Cleanup trong EVT_SIGNAL_EXIT_BOOT_SERVICES nếu cần Close event, release boot-time resource, dừng dùng gBS.
Firmware runtime driver Update pointer trong EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE Gọi ConvertPointer() cho mọi pointer nội bộ sau SetVirtualAddressMap.
OS loader Có thể gọi SetVirtualAddressMap() sau ExitBootServices Nếu OS dùng virtual mapping cho Runtime Services, báo địa chỉ virtual cho firmware runtime code. Chỉ gọi một lần.
OS Reclaim EfiBootServicesData/Code Vùng memory boot services có thể được OS dùng lại sau ExitBootServices.

Khi debug ExitBootServices fail

Nếu ExitBootServices() trả lỗi hoặc treo, nguyên nhân thường là:

  • EFI_INVALID_PARAMETER → MapKey stale hoặc ImageHandle/parameter không hợp lệ. Nguyên nhân phổ biến nhất là có allocate/free sau GetMemoryMap().
  • Hang, không return → EVT_SIGNAL_EXIT_BOOT_SERVICES handler của một driver nào đó bị treo - thường là do handler gọi Boot Services phức tạp bên trong.

Trường hợp hang thường khó debug nhất - cần xác định driver nào register handler và handler đó đang làm gì (thường là gọi Boot Services không hợp lệ bên trong handler).

Checklist ExitBootServices

Câu hỏi tự kiểm tra

  1. MapKey trong GetMemoryMap() dùng để làm gì?
  2. Tại sao không được allocate memory giữa GetMemoryMap()ExitBootServices()?
  3. ExitBootServices() trả EFI_INVALID_PARAMETER - nguyên nhân là gì?
  4. EVT_SIGNAL_EXIT_BOOT_SERVICES handler không được làm gì?
  5. Sau ExitBootServices() success, OS có thể reclaim loại memory nào?
  6. SetVirtualAddressMap() được gọi khi nào và bao nhiêu lần?
  7. Runtime driver cần đăng ký những event nào liên quan đến ExitBootServices?

Bài liên quan

Nguồn tham khảo public

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.

Biến note thành bài viết hoàn chỉnh

Notes là nơi ghi nhanh khái niệm.