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.

Cập nhật 6 phút đọc
Driver Types cover

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 NumberOfChildrenChildHandleBuffer. Đâ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

  1. Tại sao thứ tự cleanup trong Stop() phải ngược với Start()?
  2. NumberOfChildren > 0 vs = 0 - bus driver xử lý thế nào khác nhau?
  3. CR() macro dùng để làm gì trong Stop()?
  4. Nếu UninstallProtocolInterface() fail vì consumer đang giữ open, Stop() nên làm gì?
  5. Firmware destroy handle khi nào - driver có cần gọi API delete handle không?

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.