Stop() trong UEFI Driver Model là gì?
Stop() là cleanup đối xứng của Start(). Hiểu CloseProtocol, uninstall protocol, NumberOfChildren, destroy child handle và anti-pattern làm DisconnectController fail.
Stop() là hàm cleanup đối xứng với Start(). Mỗi resource được acquire trong Start() phải được release đúng trong Stop() - theo thứ tự ngược lại.
Nếu Stop() thiếu một CloseProtocol hoặc một UninstallProtocol, DisconnectController() sẽ fail hoặc để lại open count leak vĩnh viễn - controller đó không thể được reconnect hay rebind sau này.
Stop() contract - thứ tự ngược với Start()
Stop() phải thực hiện ngược thứ tự Start():
Start() làm: Stop() phải làm ngược:
─────────────────────────────────────────────────────
5. Tạo child handle → 5. Destroy child handle
4. Install protocol → 4. Uninstall protocol
3. Init hardware → 3. Deinit hardware (nếu cần)
2. Allocate context → 2. Free context
1. OpenProtocol BY_DRIVER → 1. CloseProtocol BY_DRIVER
Thứ tự cleanup phải đối xứng với resource ownership trong Start(). Protocol do driver install phải được uninstall. Protocol do driver open bằng BY_DRIVER hoặc BY_CHILD_CONTROLLER phải được close. Với bus driver, khi destroy child handle, cần hủy parent-child relationship và uninstall các protocol trên child theo đúng ownership. Nếu thứ tự sai hoặc bỏ sót một bước, DisconnectController() có thể fail hoặc để lại open count/protocol leak.
NumberOfChildren - Stop() có hai chế độ
Stop() nhận NumberOfChildren và ChildHandleBuffer. Đây là cơ chế firmware báo Stop() cần làm gì:
| Mục | Giá trị | Ghi chú |
|---|---|---|
| NumberOfChildren = 0 | Stop quản lý controller | Device driver: cleanup toàn bộ - uninstall protocol, free context, close protocol. Bus driver: thường là stop controller/bus itself; chỉ cleanup parent khi không còn child cần giữ. |
| NumberOfChildren > 0 | Stop child handle cụ thể | Bus driver: chỉ destroy các child trong ChildHandleBuffer. Không uninstall protocol trên ControllerHandle nếu còn child khác đang active. |
Bus driver phải xử lý cả hai chế độ:
EFI_STATUS EFIAPI
MyBusDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
MY_DRIVER_CONTEXT *Context;
// Recover context từ protocol (dùng CR() macro hoặc OpenProtocol GET)
// ...
if (NumberOfChildren > 0) {
// Chế độ 1: chỉ stop child handle được chỉ định
for (UINTN i = 0; i < NumberOfChildren; i++) {
Status = DestroyChildHandle(ChildHandleBuffer[i], ControllerHandle, This);
if (EFI_ERROR(Status)) {
return Status; // Caller sẽ retry hoặc report lỗi
}
}
return EFI_SUCCESS;
// Không cleanup ControllerHandle - còn có thể có child khác
}
// Chế độ 2: NumberOfChildren = 0 → stop toàn bộ
// Phải đảm bảo không còn child nào trước bước này
// Cleanup ngược thứ tự Start()
gBS->UninstallProtocolInterface(ControllerHandle, &gMyBusProtocolGuid, &Context->BusProtocol);
gBS->FreePool(Context);
gBS->CloseProtocol(ControllerHandle, &gEfiPciIoProtocolGuid,
This->DriverBindingHandle, ControllerHandle);
return EFI_SUCCESS;
}
Destroy child handle - cleanup đầy đủ
Với mỗi child handle cần destroy, thứ tự cleanup phải ngược với Start():
EFI_STATUS
DestroyChildHandle (
IN EFI_HANDLE ChildHandle,
IN EFI_HANDLE ControllerHandle,
IN EFI_DRIVER_BINDING_PROTOCOL *This
)
{
// pseudo-code rút gọn - code thật phải check Status từng bước
// 1. Close BY_CHILD_CONTROLLER trên parent - hủy parent-child relationship
gBS->CloseProtocol(
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ChildHandle // ControllerHandle = child handle trong call này
);
// 2. Uninstall bus-specific protocol khỏi child handle
gBS->UninstallProtocolInterface(
ChildHandle,
&gMyBusChildProtocolGuid,
&Context->ChildProtocol
);
// 3. Uninstall Device Path khỏi child handle
gBS->UninstallProtocolInterface(
ChildHandle,
&gEfiDevicePathProtocolGuid,
ChildDevicePath
);
// 4. Free child device path nếu allocate riêng
if (ChildDevicePath != NULL) {
gBS->FreePool(ChildDevicePath);
}
return EFI_SUCCESS;
}
Recover context trong Stop() - từ protocol pointer
Stop() nhận ControllerHandle nhưng không nhận context trực tiếp. Cần recover context từ protocol đã install trong Start(). Hai cách phổ biến trong EDK II:
// Cách 1: CR() macro - chuẩn trong EDK II
// Context chứa protocol struct, CR() recover context từ protocol pointer
MY_DRIVER_CONTEXT *Context;
EFI_MY_PROTOCOL *MyProtocol;
// Lấy protocol pointer - phải check Status
Status = gBS->OpenProtocol(
ControllerHandle,
&gMyProtocolGuid,
(VOID **)&MyProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL // chỉ lấy interface, không claim
);
if (EFI_ERROR(Status)) {
return EFI_NOT_STARTED; // Protocol không tồn tại → Stop() không thể recover context
}
// CR() macro recover context từ field trong struct
// MY_DRIVER_CONTEXT_FROM_PROTOCOL(MyProtocol) hoặc
Context = CR(MyProtocol, MY_DRIVER_CONTEXT, MyProtocol, MY_DRIVER_SIGNATURE);
// Cách 2: Dùng private data trực tiếp nếu có global/list
// Hoặc lưu context pointer trong protocol field riêng
Context = MyProtocol->Private; // nếu protocol struct có field Private
CR() macro là pattern EDK II chuẩn, giúp recover context an toàn với signature check.
Anti-pattern thực tế
Anti-pattern 1: Thiếu CloseProtocol BY_CHILD_CONTROLLER
// SAI - destroy child handle mà không close BY_CHILD_CONTROLLER trước
gBS->UninstallProtocolInterface(ChildHandle, &gMyChildProtocol, ...);
// Open count BY_CHILD_CONTROLLER trên ControllerHandle vẫn còn
// DisconnectController() sau đó có thể fail vì open count không về 0
Anti-pattern 2: Không handle NumberOfChildren = 0 vs > 0
// SAI - bus driver luôn uninstall protocol trên ControllerHandle
// dù NumberOfChildren > 0 và còn child khác đang active
gBS->UninstallProtocolInterface(ControllerHandle, &gMyBusProtocol, ...);
gBS->CloseProtocol(ControllerHandle, &gEfiPciIoProtocolGuid, ...);
// Những child còn lại mất đi parent protocol - orphaned
Anti-pattern 3: Recover context sai, Stop() free nhầm
// SAI - dùng static/global context thay vì recover đúng từ handle
extern MY_DRIVER_CONTEXT gStaticContext;
gBS->FreePool(&gStaticContext); // Crash nếu gStaticContext không phải heap allocation
Anti-pattern 4: Return lỗi giữa chừng mà không hoàn thành cleanup
Status = gBS->UninstallProtocolInterface(...);
if (EFI_ERROR(Status)) {
return Status; // LEAK - CloseProtocol chưa được gọi
}
gBS->CloseProtocol(...);
Nếu UninstallProtocolInterface() fail vì protocol còn consumer đang open, không được free context hoặc close ownership một cách mù quáng. Cần giữ state nhất quán và return lỗi để caller retry sau khi consumer được disconnect, hoặc thực hiện best-effort cleanup theo policy rõ ràng.
DisconnectController fail - trace từ Stop()
Khi DisconnectController() fail, thường nguyên nhân là Stop() không cleanup đúng ở lần trước hoặc consumer đang giữ open:
# UEFI Shell: xem open count trên controller handle
Shell> openinfo <handle>
# Nếu còn BY_DRIVER hoặc BY_CHILD_CONTROLLER open sau Stop()
# → Stop() chưa CloseProtocol đúng
# Xem device tree - child handle còn tồn tại không
Shell> devtree
# Nếu child handle còn sau Stop() → UninstallProtocol chưa đủ
# Disconnect thủ công để test
Shell> disconnect <controller_handle> <driver_handle>
# EDK II trace
grep -r "CoreDisconnectController\|DisconnectController" \
edk2/MdeModulePkg/Core/Dxe/ --include="*.c" | head -10
Checklist Stop()
Câu hỏi tự kiểm tra
- Tại sao thứ tự cleanup trong
Stop()phải ngược vớiStart()? NumberOfChildren > 0vs= 0- bus driver xử lý thế nào khác nhau?- CR() macro dùng để làm gì trong
Stop()? - Nếu
UninstallProtocolInterface()fail vì consumer đang giữ open,Stop()nên làm gì? - Firmware destroy handle khi nào - driver có cần gọi API delete handle không?
Bài liên quan
- Start() trong UEFI Driver Model là gì?
- Supported() trong UEFI Driver Model là gì?
- Driver Binding Flow là gì?
- Handle Database là gì?
- Protocol là gì?
Nguồn tham khảo public
- UEFI Specification 2.11 - EFI_DRIVER_BINDING_PROTOCOL.Stop()
- EDK II UEFI Driver Writer’s Guide - Stop()
- EDK II - DriverBinding.h
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.
Start() trong UEFI Driver Model là gì?
Start() là nơi driver bind vào controller: open BY_DRIVER, install protocol, tạo child handle nếu là bus driver. Hiểu cleanup fail path và anti-pattern làm handle database bẩn.
Driver Binding Flow là gì?
Giải thích Supported, Start, Stop trong UEFI Driver Model và cách nó ảnh hưởng quá trình enumerate thiết bị.
Supported() trong UEFI Driver Model là gì?
Supported() là hàm probe của Driver Binding. Hiểu return status, OpenProtocol attribute đúng, anti-pattern làm driver không bao giờ bind và cách trace khi Supported fail.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.