Mise à niveau du microprogramme pour un appareil NVMe
Les mises à jour du microprogramme sur un périphérique de stockage NVMe sont émises au pilote miniport pour cet appareil. Les commandes de fonction permettant d’obtenir des informations sur le microprogramme, de télécharger et d’activer des images de microprogramme sont émises à la miniporte.
Processus de mise à niveau du microprogramme
Les appareils NVMe certifiés pour Windows sont capables de mettre à jour leur microprogramme pendant que l’appareil est en cours d’opération. Le microprogramme est mis à jour à l’aide de la requête IOCTL_SCSI_MINIPORT contenant les données de contrôle de microprogramme associées mises en forme dans un SRB. Le processus de mise à jour implique :
Rassemblez les informations d’emplacement du microprogramme pour déterminer où placer la mise à jour. Il existe quelques considérations à prendre en compte pour décider où placer la mise à jour du microprogramme, par exemple :
- Combien d’emplacements sont disponibles ?
- Combien d’emplacements peuvent contenir une mise à jour ? Certains emplacements sont en lecture seule ou contiennent des images qui doivent être conservées si la possibilité de revenir à une image antérieure est souhaitée.
- Quel emplacement contient l’image actuelle du microprogramme actif (microprogramme en cours d’exécution) ?
Pour mettre à jour l’appareil, un emplacement est choisi qui est accessible en écriture et non actif actuellement. Toutes les données d’image existantes dans l’emplacement sélectionné sont remplacées lorsque la mise à jour est terminée.
Téléchargez la nouvelle image du microprogramme pour un emplacement sélectionné. Selon la taille de l’image, le téléchargement se produit dans une opération de transfert unique ou dans les transferts successifs de plusieurs parties de l’image. Une partie d’une image est limitée par min(Taille de transfert maximale du contrôleur, 512 Ko).
Pour que l’image téléchargée soit l’image de microprogramme active, son numéro d’emplacement est affecté à l’emplacement dans lequel il a été téléchargé. L’emplacement du microprogramme actif passe ensuite de l’emplacement actuellement utilisé à l’emplacement affecté à l’image téléchargée. Selon le type de téléchargement et les modifications apportées à l’image du microprogramme, un redémarrage du système peut être nécessaire. Le contrôleur NVMe détermine si un redémarrage est nécessaire.
Demandes de contrôle du microprogramme miniport
Chaque commande de fonction est définie dans une structure FIRMWARE_REQUEST_BLOCK incluse dans une SRB_IO_CONTROL dans la mémoire tampon d’une requête IOCTL_SCSI_MINIPORT. Le membre ControlCode de SRB_IO_CONTROL est défini sur IOCTL_SCSI_MINIPORT_FIRMWARE pour indiquer une opération de microprogramme miniport. Chaque commande de fonction a une structure d’informations associée située après le FIRMWARE_REQUEST_BLOCK. Le tableau suivant répertorie chaque commande de fonction et les structures incluses dans la mémoire tampon système pour IOCTL_SCSI_MINIPORT.
Fonction | Données d’entrée | Données de sortie |
---|---|---|
FIRMWARE_FUNCTION_GET_INFO | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_SLOT_INFO |
FIRMWARE_FUNCTION_DOWNLOAD | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_DOWNLOAD | SRB_IO_CONTROL |
FIRMWARE_FUNCTION_ACTIVATE | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_ACTIVATE | SRB_IO_CONTROL |
Les fonctions du microprogramme et les structures associées sont définies dans ntddscsi.h.
Informations sur l’emplacement du microprogramme
Les images de microprogramme sont conservées sur l’appareil dans des emplacements appelés emplacements. Il est nécessaire de trouver un emplacement disponible pour que l’image du microprogramme réside lorsque l’image du microprogramme est activée après un téléchargement. Pour trouver un emplacement disponible, un utilitaire de mise à niveau peut envoyer une requête d’informations à l’appareil pour recevoir les descripteurs d’informations d’emplacement. L’exemple de fonction suivant montre comment récupérer les informations de tous les emplacements de microprogramme sur un appareil NVMe sélectionné.
// A device list item structure for an adapter
typedef struct _DEVICE_LIST {
HANDLE Handle;
STORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
} DEVICE_LIST, *PDEVICE_LIST;
BOOL
DeviceGetFirmwareInfo(
_In_ PDEVICE_LIST DeviceList,
_In_ DWORD Index,
_Inout_ PUCHAR Buffer,
_In_ DWORD BufferLength,
_In_ BOOLEAN DisplayResult
)
/*++
Routine Description:
Retrieve the firmware and firmware slot information from NVMe controller.
Arguments:
DeviceList – a pointer to device array that contains disks information.
Index – the index of NVMe device in DeviceList array.
Buffer – a buffer for input and output.
BufferLength – the size of the buffer.
DisplayResult – print information on screen or not.
Return Value:
BOOLEAN
--*/
{
BOOL result;
ULONG returnedLength;
ULONG firmwareInfoOffset;
PSRB_IO_CONTROL srbControl;
PFIRMWARE_REQUEST_BLOCK firmwareRequest;
PSTORAGE_FIRMWARE_INFO firmwareInfo;
srbControl = (PSRB_IO_CONTROL)Buffer;
firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1);
//
// The STORAGE_FIRMWARE_INFO is located after SRB_IO_CONTROL and FIRMWARE_REQUEST_BLOCK
//
firmwareInfoOffset = ((sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK) - 1) / sizeof(PVOID) + 1) * sizeof(PVOID);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = BufferLength - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_GET_INFO. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_GET_INFO;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareInfoOffset;
firmwareRequest->DataBufferLength = BufferLength - firmwareInfoOffset;
//
// Send the request to get the device firmware info
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
Buffer,
BufferLength,
Buffer,
BufferLength,
&returnedLength,
NULL
);
//
// Format and display the firmware info
//
if (DisplayResult) {
if (!result) {
_tprintf(_T("\t Get Firmware Information Failed: 0x%X\n"), GetLastError());
} else {
UCHAR i;
TCHAR revision[16] = {0};
firmwareInfo = (PSTORAGE_FIRMWARE_INFO)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
_tprintf(_T("\t ----Firmware Information----\n"));
_tprintf(_T("\t Support upgrade command: %s\n"), firmwareInfo->UpgradeSupport ? _T("Yes") : _T("No"));
_tprintf(_T("\t Slot Count: %d\n"), firmwareInfo->SlotCount);
_tprintf(_T("\t Current Active Slot: %d\n"), firmwareInfo->ActiveSlot);
if (firmwareInfo->PendingActivateSlot == STORAGE_FIRMWARE_INFO_INVALID_SLOT) {
_tprintf(_T("\t Pending Active Slot: %s\n\n"), _T("No"));
} else {
_tprintf(_T("\t Pending Active Slot: %d\n\n"), firmwareInfo->PendingActivateSlot);
}
for (i = 0; i < firmwareInfo->SlotCount; i++) {
RtlCopyMemory(revision, &firmwareInfo->Slot[i].Revision.AsUlonglong, 8);
_tprintf(_T("\t\t Slot Number: %d\n"), firmwareInfo->Slot[i].SlotNumber);
_tprintf(_T("\t\t Slot Read Only: %s\n"), firmwareInfo->Slot[i].ReadOnly ? _T("Yes") : _T("No"));
_tprintf(_T("\t\t Revision: %s\n"), revision);
_tprintf(_T("\n"));
}
}
_tprintf(_T("\n"));
}
return result;
}
Les informations d’emplacement sont retournées dans un tableau de structures STORAGE_FIRMWARE_SLOT_INFO . Chaque structure indique l’état d’activation et la disponibilité de l’emplacement du microprogramme. Les conditions de disponibilité sont les suivantes :
- Le membre ReadOnly est défini sur 0.
- L’emplacement n’est pas l’emplacement actif indiqué par le numéro d’emplacement dans le membre ActiveSlot de STORAGE_FIRMWARE_INFO.
- Le membre PendingActiveSlot de STORAGE_FIRMWARE_INFO est défini sur STORAGE_FIRMWARE_INFO_INVALID_SLOT.
- Le membre PendingActiveSlot de STORAGE_FIRMWARE_INFO n’est pas défini sur l’emplacement souhaité.
En outre, si l’état de l’emplacement répond aux conditions de disponibilité, mais que la chaîne d’informations contient des données de révision valides qui ne contiennent pas de zéro octets, l’emplacement contient une image de microprogramme valide, mais elle peut être remplacée. Tous les zéros de la chaîne d’informations indiquent un emplacement vide.
Exemple : mise à niveau du microprogramme - sélection, téléchargement et activation de l’emplacement
Un utilitaire de mise à niveau effectue les trois étapes mentionnées précédemment pour mettre à jour le microprogramme dans le contrôleur. Par exemple, la routine de mise à niveau suivante contient du code pour chaque étape du processus. L’étape de découverte de l’emplacement, illustrée dans l’exemple DeviceGetFirmwareInfo , est appelée par la routine de mise à niveau pour sélectionner un emplacement disponible. Les étapes de téléchargement et d’activation de l’image sont illustrées directement après la sélection de l’emplacement. Dans chaque étape, l’utilisation de la commande de fonction correspondante s’affiche.
Au cours de l’étape de téléchargement, un fichier image du microprogramme est lu dans une mémoire tampon allouée et le contenu de la mémoire tampon est transféré au contrôleur. Si le fichier image du microprogramme est supérieur à la taille de la mémoire tampon, le fichier image est lu plusieurs fois avec une partie de l’image transférée chaque fois jusqu’à ce que l’intégralité du fichier soit lu.
Après la fin du téléchargement de l’image du microprogramme, l’étape d’activation nécessite deux actions du contrôleur. Tout d’abord, l’emplacement sélectionné est affecté à l’image du microprogramme, et deuxièmement, l’emplacement sélectionné est défini comme emplacement actif.
VOID
DeviceFirmwareUpgrade(
_In_ PDEVICE_LIST DeviceList,
_In_ DWORD Index,
_In_ TCHAR* FileName
)
/*++
Routine Description:
Performs a firmware upgrade to the NVMe controller. The an available firmware
slot is selected, the firmware is downloaded to the controller from an image
file, and the new firmware is activated.
Arguments:
DeviceList – a pointer to device array that contains disks information.
Index – the index of NVMe device in DeviceList array.
FileName – the name of the firmware upgrade image file.
Return Value:
None
--*/
{
BOOL result;
PUCHAR buffer = NULL;
ULONG bufferSize;
ULONG firmwareStructureOffset;
ULONG imageBufferLength;
PSRB_IO_CONTROL srbControl;
PFIRMWARE_REQUEST_BLOCK firmwareRequest;
PSTORAGE_FIRMWARE_INFO firmwareInfo;
PSTORAGE_FIRMWARE_DOWNLOAD firmwareDownload;
PSTORAGE_FIRMWARE_ACTIVATE firmwareActivate;
ULONG slotNumber;
ULONG returnedLength;
ULONG i;
HANDLE fileHandle = NULL;
ULONG imageOffset;
ULONG readLength;
BOOLEAN moreToDownload;
//
// The STORAGE_FIRMWARE_INFO is located after SRB_IO_CONTROL and FIRMWARE_RESQUEST_BLOCK
//
firmwareStructureOffset = ((sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK) - 1) / sizeof(PVOID) + 1) * sizeof(PVOID);
//
// The Max Transfer Length limits the part of buffer that may need to transfer to controller, not the whole buffer.
//
bufferSize = min(DeviceList[Index].AdapterDescriptor.MaximumTransferLength, 2 * 1024 * 1024);
bufferSize += firmwareStructureOffset;
bufferSize += FIELD_OFFSET(STORAGE_FIRMWARE_DOWNLOAD, ImageBuffer);
buffer = (PUCHAR)malloc(bufferSize);
if (buffer == NULL) {
_tprintf(_T("\t FirmwareUpgrade - Allocate buffer failed: 0x%X\n"), GetLastError());
return;
}
//
// calculate the space available for the firmware image portion of the buffer allocation
//
imageBufferLength = bufferSize - firmwareStructureOffset - sizeof(STORAGE_FIRMWARE_DOWNLOAD);
RtlZeroMemory(buffer, bufferSize);
// ---------------------------------------------------------------------------
// ( 1 ) SELECT A SUITABLE FIRMWARE SLOT
// ---------------------------------------------------------------------------
//
// Get firmware slot information data.
//
result = DeviceGetFirmwareInfo(DeviceList, Index, buffer, bufferSize, FALSE);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade: Get Firmware Information Failed: 0x%X\n"), GetLastError());
goto Exit;
}
//
// Set the request structure pointers
//
srbControl = (PSRB_IO_CONTROL)buffer;
firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1);
firmwareInfo = (PSTORAGE_FIRMWARE_INFO)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
if (srbControl->ReturnCode != FIRMWARE_STATUS_SUCCESS) {
_tprintf(_T("\t FirmwareUpgrade - get firmware info failed. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
goto Exit;
}
//
// SelectFind the first writable slot.
//
slotNumber = (ULONG)-1;
if (firmwareInfo->UpgradeSupport) {
for (i = 0; i < firmwareInfo->SlotCount; i++) {
if (firmwareInfo->Slot[i].ReadOnly == FALSE) {
slotNumber = firmwareInfo->Slot[i].SlotNumber;
break;
}
}
}
//
// If no writable slot is found, bypass downloading and activation
//
if (slotNumber == (ULONG)-1) {
_tprintf(_T("\t FirmwareUpgrade - No writable Firmware slot.\n"));
goto Exit;
}
// ---------------------------------------------------------------------------
// ( 2 ) DOWNLOAD THE FIRMWARE IMAGE TO THE CONTROLLER
// ---------------------------------------------------------------------------
//
// initialize image length and offset
//
imageBufferLength = (imageBufferLength / sizeof(PVOID)) * sizeof(PVOID);
imageOffset = 0;
readLength = 0;
moreToDownload = TRUE;
//
// Open image file and download it to controller.
//
if (FileName == NULL) {
_tprintf(_T("\t FirmwareUpgrade - No firmware file specified.\n"));
goto Exit;
}
fileHandle = CreateFile(FileName, // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (fileHandle == INVALID_HANDLE_VALUE) {
_tprintf(_T("\t FirmwareUpgrade - unable to open file \"%s\" for read.\n"), FileName);
goto Exit;
}
//
// Read and download the firmware from the image file into image buffer length portions. Send the
// image portion to the controller.
//
while (moreToDownload) {
RtlZeroMemory(buffer, bufferSize);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = bufferSize - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_DOWNLOAD. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_DOWNLOAD;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareStructureOffset;
firmwareRequest->DataBufferLength = bufferSize - firmwareStructureOffset;
//
// Initialize the firmware data buffer pointer to the proper position after the request structure
//
firmwareDownload = (PSTORAGE_FIRMWARE_DOWNLOAD)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
if (ReadFile(fileHandle, firmwareDownload->ImageBuffer, imageBufferLength, &readLength, NULL) == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - Read firmware file failed.\n"));
goto Exit;
}
if (readLength == 0) {
moreToDownload = FALSE;
break;
}
if ((readLength % sizeof(ULONG)) != 0) {
_tprintf(_T("\t FirmwareUpgrade - Read firmware file failed.\n"));
}
//
// Set the download parameters and adjust the offset for this portion of the firmware image
//
firmwareDownload->Version = 1;
firmwareDownload->Size = sizeof(STORAGE_FIRMWARE_DOWNLOAD);
firmwareDownload->Offset = imageOffset;
firmwareDownload->BufferSize = readLength;
//
// download this portion of firmware to the device
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
buffer,
bufferSize,
buffer,
bufferSize,
&returnedLength,
NULL
);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - IOCTL - firmware download failed. 0x%X.\n"), GetLastError());
goto Exit;
}
if (srbControl->ReturnCode != FIRMWARE_STATUS_SUCCESS) {
_tprintf(_T("\t FirmwareUpgrade - firmware download failed. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
goto Exit;
}
//
// Update Image Offset for next iteration.
//
imageOffset += readLength;
}
// ---------------------------------------------------------------------------
// ( 3 ) ACTIVATE THE FIRMWARE SLOT ASSIGNED TO THE UPGRADE
// ---------------------------------------------------------------------------
//
// Activate the newly downloaded image with the assigned slot.
//
RtlZeroMemory(buffer, bufferSize);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = bufferSize - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_ACTIVATE. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_ACTIVATE;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareStructureOffset;
firmwareRequest->DataBufferLength = bufferSize - firmwareStructureOffset;
//
// Initialize the firmware activation structure pointer to the proper position after the request structure
//
firmwareActivate = (PSTORAGE_FIRMWARE_ACTIVATE)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
//
// Set the activation parameters with the available slot selected
//
firmwareActivate->Version = 1;
firmwareActivate->Size = sizeof(STORAGE_FIRMWARE_ACTIVATE);
firmwareActivate->SlotToActivate = (UCHAR)slotNumber;
//
// Send the activation request
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
buffer,
bufferSize,
buffer,
bufferSize,
&returnedLength,
NULL
);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - IOCTL - firmware activate failed. 0x%X.\n"), GetLastError());
goto Exit;
}
//
// Display status result from firmware activation
//
switch (srbControl->ReturnCode) {
case FIRMWARE_STATUS_SUCCESS:
_tprintf(_T("\t FirmwareUpgrade - firmware activate succeeded.\n"));
break;
case FIRMWARE_STATUS_POWER_CYCLE_REQUIRED:
_tprintf(_T("\t FirmwareUpgrade - firmware activate succeeded. PLEASE REBOOT COMPUTER.\n"));
break;
case FIRMWARE_STATUS_ILLEGAL_REQUEST:
case FIRMWARE_STATUS_INVALID_PARAMETER:
case FIRMWARE_STATUS_INPUT_BUFFER_TOO_BIG:
_tprintf(_T("\t FirmwareUpgrade - firmware activate parameter error. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
break;
case FIRMWARE_STATUS_INVALID_SLOT:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, slot number invalid.\n"));
break;
case FIRMWARE_STATUS_INVALID_IMAGE:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, invalid firmware image.\n"));
break;
case FIRMWARE_STATUS_ERROR:
case FIRMWARE_STATUS_CONTROLLER_ERROR:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, error returned.\n"));
break;
default:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, unexpected error. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
break;
}
Exit:
if (fileHandle != NULL) {
CloseHandle(fileHandle);
}
if (buffer != NULL) {
free(buffer);
}
return;
}
Remarque
Le téléchargement simultané de plusieurs images de microprogramme n’est pas pris en charge. Un téléchargement de microprogramme unique est toujours suivi d’une activation unique du microprogramme.
Une image de microprogramme déjà résident dans un emplacement peut être réactivée à l’aide de la commande activer la fonction avec le numéro d’emplacement correspondant.
Le code de contrôle IOCTL_SCSI_MINIPORT_FIRMWARE pour le contrôle d’E/SRB est disponible à partir de Windows 8.1.