1
0
Fork 0

Adding upstream version 2.12.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-03-20 08:10:40 +01:00
parent 65508f0a28
commit c0fbec1eb4
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
571 changed files with 10718 additions and 2738 deletions

View file

@ -1882,7 +1882,6 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
case WDC_NVME_SN861_DEV_ID:
case WDC_NVME_SN861_DEV_ID_1:
case WDC_NVME_SN861_DEV_ID_2:
case WDC_NVME_SNTMP_DEV_ID:
capabilities |= (WDC_DRIVE_CAP_C0_LOG_PAGE |
WDC_DRIVE_CAP_C3_LOG_PAGE |
WDC_DRIVE_CAP_CA_LOG_PAGE |
@ -1899,6 +1898,23 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
WDC_DRIVE_CAP_SET_LATENCY_MONITOR);
break;
case WDC_NVME_SNTMP_DEV_ID:
capabilities |= (WDC_DRIVE_CAP_C0_LOG_PAGE |
WDC_DRIVE_CAP_C3_LOG_PAGE |
WDC_DRIVE_CAP_CA_LOG_PAGE |
WDC_DRIVE_CAP_OCP_C4_LOG_PAGE |
WDC_DRIVE_CAP_OCP_C5_LOG_PAGE |
WDC_DRIVE_CAP_DUI |
WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 |
WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE |
WDC_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY |
WDC_DRIVE_CAP_INFO |
WDC_DRIVE_CAP_CLOUD_SSD_VERSION |
WDC_DRIVE_CAP_LOG_PAGE_DIR |
WDC_DRIVE_CAP_DRIVE_STATUS |
WDC_DRIVE_CAP_SET_LATENCY_MONITOR);
break;
default:
capabilities = 0;
}
@ -2165,6 +2181,163 @@ static int wdc_create_log_file(char *file, __u8 *drive_log_data,
return 0;
}
bool wdc_validate_dev_mng_log(void *data)
{
__u32 remaining_len = 0;
__u32 log_length = 0;
__u32 log_entry_size = 0;
__u32 log_entry_id = 0;
__u32 offset = 0;
bool valid_log = false;
struct wdc_c2_log_subpage_header *p_next_log_entry = NULL;
struct wdc_c2_log_page_header *hdr_ptr = (struct wdc_c2_log_page_header *)data;
log_length = le32_to_cpu(hdr_ptr->length);
/* Ensure log data is large enough for common header */
if (log_length < sizeof(struct wdc_c2_log_page_header)) {
fprintf(stderr,
"ERROR: %s: log smaller than header. log_len: 0x%x HdrSize: %"PRIxPTR"\n",
__func__, log_length, sizeof(struct wdc_c2_log_page_header));
return valid_log;
}
/* Get pointer to first log Entry */
offset = sizeof(struct wdc_c2_log_page_header);
p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8 *)data) + offset);
remaining_len = log_length - offset;
/* Proceed only if there is at least enough data to read an entry header */
while (remaining_len >= sizeof(struct wdc_c2_log_subpage_header)) {
/* Get size of the next entry */
log_entry_size = le32_to_cpu(p_next_log_entry->length);
log_entry_id = le32_to_cpu(p_next_log_entry->entry_id);
/*
* If log entry size is 0 or the log entry goes past the end
* of the data, we must be at the end of the data
*/
if (!log_entry_size || log_entry_size > remaining_len) {
fprintf(stderr, "ERROR: WDC: %s: Detected unaligned end of the data. ",
__func__);
fprintf(stderr, "Data Offset: 0x%x Entry Size: 0x%x, ",
offset, log_entry_size);
fprintf(stderr, "Remaining Log Length: 0x%x Entry Id: 0x%x\n",
remaining_len, log_entry_id);
/* Force the loop to end */
remaining_len = 0;
} else if (!log_entry_id || log_entry_id > 200) {
/* Invalid entry - fail the search */
fprintf(stderr, "ERROR: WDC: %s: Invalid entry found at offset: 0x%x ",
__func__, offset);
fprintf(stderr, "Entry Size: 0x%x, Remaining Log Length: 0x%x ",
log_entry_size, remaining_len);
fprintf(stderr, "Entry Id: 0x%x\n", log_entry_id);
/* Force the loop to end */
remaining_len = 0;
valid_log = false;
} else {
/* A valid log has at least one entry and no invalid entries */
valid_log = true;
remaining_len -= log_entry_size;
if (remaining_len > 0) {
/* Increment the offset counter */
offset += log_entry_size;
/* Get the next entry */
p_next_log_entry =
(struct wdc_c2_log_subpage_header *)(((__u8 *)data) + offset);
}
}
}
return valid_log;
}
bool wdc_parse_dev_mng_log_entry(void *data, __u32 entry_id,
struct wdc_c2_log_subpage_header **log_entry)
{
__u32 remaining_len = 0;
__u32 log_length = 0;
__u32 log_entry_size = 0;
__u32 log_entry_id = 0;
__u32 offset = 0;
bool found = false;
struct wdc_c2_log_subpage_header *p_next_log_entry = NULL;
struct wdc_c2_log_page_header *hdr_ptr = (struct wdc_c2_log_page_header *)data;
log_length = le32_to_cpu(hdr_ptr->length);
/* Ensure log data is large enough for common header */
if (log_length < sizeof(struct wdc_c2_log_page_header)) {
fprintf(stderr,
"ERROR: %s: log smaller than header. log_len: 0x%x HdrSize: %"PRIxPTR"\n",
__func__, log_length, sizeof(struct wdc_c2_log_page_header));
return found;
}
/* Get pointer to first log Entry */
offset = sizeof(struct wdc_c2_log_page_header);
p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8 *)data) + offset);
remaining_len = log_length - offset;
if (!log_entry) {
fprintf(stderr, "ERROR: WDC - %s: No log entry pointer.\n", __func__);
return found;
}
*log_entry = NULL;
/* Proceed only if there is at least enough data to read an entry header */
while (remaining_len >= sizeof(struct wdc_c2_log_subpage_header)) {
/* Get size of the next entry */
log_entry_size = le32_to_cpu(p_next_log_entry->length);
log_entry_id = le32_to_cpu(p_next_log_entry->entry_id);
/*
* If log entry size is 0 or the log entry goes past the end
* of the data, we must be at the end of the data
*/
if (!log_entry_size || log_entry_size > remaining_len) {
fprintf(stderr, "ERROR: WDC: %s: Detected unaligned end of the data. ",
__func__);
fprintf(stderr, "Data Offset: 0x%x Entry Size: 0x%x, ",
offset, log_entry_size);
fprintf(stderr, "Remaining Log Length: 0x%x Entry Id: 0x%x\n",
remaining_len, log_entry_id);
/* Force the loop to end */
remaining_len = 0;
} else if (!log_entry_id || log_entry_id > 200) {
/* Invalid entry - fail the search */
fprintf(stderr, "ERROR: WDC: %s: Invalid entry found at offset: 0x%x ",
__func__, offset);
fprintf(stderr, "Entry Size: 0x%x, Remaining Log Length: 0x%x ",
log_entry_size, remaining_len);
fprintf(stderr, "Entry Id: 0x%x\n", log_entry_id);
/* Force the loop to end */
remaining_len = 0;
} else {
if (log_entry_id == entry_id) {
found = true;
*log_entry = p_next_log_entry;
remaining_len = 0;
} else {
remaining_len -= log_entry_size;
}
if (remaining_len > 0) {
/* Increment the offset counter */
offset += log_entry_size;
/* Get the next entry */
p_next_log_entry =
(struct wdc_c2_log_subpage_header *)(((__u8 *)data) + offset);
}
}
}
return found;
}
bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
struct wdc_c2_log_page_header *p_log_hdr,
struct wdc_c2_log_subpage_header **p_p_found_log_entry)
@ -2172,9 +2345,10 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
__u32 remaining_len = 0;
__u32 log_entry_hdr_size = sizeof(struct wdc_c2_log_subpage_header) - 1;
__u32 log_entry_size = 0;
__u32 log_entry_id = 0;
__u32 size = 0;
bool valid_log;
__u32 current_data_offset = 0;
__u32 offset = 0;
struct wdc_c2_log_subpage_header *p_next_log_entry = NULL;
if (!*p_p_found_log_entry) {
@ -2194,8 +2368,8 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
/* Get pointer to first log Entry */
size = sizeof(struct wdc_c2_log_page_header);
current_data_offset = size;
p_next_log_entry = (struct wdc_c2_log_subpage_header *)((__u8 *)p_log_hdr + current_data_offset);
offset = size;
p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8 *)p_log_hdr) + offset);
remaining_len = log_length - size;
valid_log = false;
@ -2209,7 +2383,8 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
/* Proceed only if there is at least enough data to read an entry header */
while (remaining_len >= log_entry_hdr_size) {
/* Get size of the next entry */
log_entry_size = p_next_log_entry->length;
log_entry_size = le32_to_cpu(p_next_log_entry->length);
log_entry_id = le32_to_cpu(p_next_log_entry->entry_id);
/*
* If log entry size is 0 or the log entry goes past the end
@ -2219,19 +2394,19 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
fprintf(stderr, "ERROR: WDC: %s: Detected unaligned end of the data. ",
__func__);
fprintf(stderr, "Data Offset: 0x%x Entry Size: 0x%x, ",
current_data_offset, log_entry_size);
offset, log_entry_size);
fprintf(stderr, "Remaining Log Length: 0x%x Entry Id: 0x%x\n",
remaining_len, p_next_log_entry->entry_id);
remaining_len, log_entry_id);
/* Force the loop to end */
remaining_len = 0;
} else if (!p_next_log_entry->entry_id || p_next_log_entry->entry_id > 200) {
} else if (!log_entry_id || log_entry_id > 200) {
/* Invalid entry - fail the search */
fprintf(stderr, "ERROR: WDC: %s: Invalid entry found at offset: 0x%x ",
__func__, current_data_offset);
__func__, offset);
fprintf(stderr, "Entry Size: 0x%x, Remaining Log Length: 0x%x ",
log_entry_size, remaining_len);
fprintf(stderr, "Entry Id: 0x%x\n", p_next_log_entry->entry_id);
fprintf(stderr, "Entry Id: 0x%x\n", log_entry_id);
/* Force the loop to end */
remaining_len = 0;
@ -2242,7 +2417,7 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
} else {
/* Structure must have at least one valid entry to be considered valid */
valid_log = true;
if (p_next_log_entry->entry_id == entry_id)
if (log_entry_id == entry_id)
/* A potential match. */
*p_p_found_log_entry = p_next_log_entry;
@ -2250,10 +2425,11 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
if (remaining_len > 0) {
/* Increment the offset counter */
current_data_offset += log_entry_size;
offset += log_entry_size;
/* Get the next entry */
p_next_log_entry = (struct wdc_c2_log_subpage_header *)(((__u8 *)p_log_hdr) + current_data_offset);
p_next_log_entry =
(struct wdc_c2_log_subpage_header *)(((__u8 *)p_log_hdr) + offset);
}
}
}
@ -2261,6 +2437,109 @@ bool wdc_get_dev_mng_log_entry(__u32 log_length, __u32 entry_id,
return valid_log;
}
static bool get_dev_mgmt_log_page_data(struct nvme_dev *dev, void **log_data,
__u8 uuid_ix)
{
void *data;
struct wdc_c2_log_page_header *hdr_ptr;
__u32 length = 0;
int ret = 0;
bool valid = false;
data = (__u8 *)malloc(sizeof(__u8) * WDC_C2_LOG_BUF_LEN);
if (!data) {
fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno));
return false;
}
memset(data, 0, sizeof(__u8) * WDC_C2_LOG_BUF_LEN);
/* get the log page length */
struct nvme_get_log_args args_len = {
.args_size = sizeof(args_len),
.fd = dev_fd(dev),
.lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID,
.nsid = 0xFFFFFFFF,
.lpo = 0,
.lsp = NVME_LOG_LSP_NONE,
.lsi = 0,
.rae = false,
.uuidx = uuid_ix,
.csi = NVME_CSI_NVM,
.ot = false,
.len = WDC_C2_LOG_BUF_LEN,
.log = data,
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
.result = NULL,
};
ret = nvme_get_log(&args_len);
if (ret) {
fprintf(stderr,
"ERROR: WDC: Unable to get 0x%x Log Page with uuid %d, ret = 0x%x\n",
WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID, uuid_ix, ret);
goto end;
}
hdr_ptr = (struct wdc_c2_log_page_header *)data;
length = le32_to_cpu(hdr_ptr->length);
if (length > WDC_C2_LOG_BUF_LEN) {
/* Log page buffer too small for actual data */
free(data);
data = calloc(length, sizeof(__u8));
if (!data) {
fprintf(stderr, "ERROR: WDC: malloc: %s\n", strerror(errno));
goto end;
}
/* get the log page data with the increased length */
struct nvme_get_log_args args_data = {
.args_size = sizeof(args_data),
.fd = dev_fd(dev),
.lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID,
.nsid = 0xFFFFFFFF,
.lpo = 0,
.lsp = NVME_LOG_LSP_NONE,
.lsi = 0,
.rae = false,
.uuidx = uuid_ix,
.csi = NVME_CSI_NVM,
.ot = false,
.len = length,
.log = data,
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
.result = NULL,
};
ret = nvme_get_log(&args_data);
if (ret) {
fprintf(stderr,
"ERROR: WDC: Unable to read 0x%x Log with uuid %d, ret = 0x%x\n",
WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_ID, uuid_ix, ret);
goto end;
}
}
valid = wdc_validate_dev_mng_log(data);
if (valid) {
/* Ensure size of log data matches length in log header */
*log_data = calloc(length, sizeof(__u8));
if (!*log_data) {
fprintf(stderr, "ERROR: WDC: calloc: %s\n", strerror(errno));
valid = false;
goto end;
}
memcpy((void *)*log_data, data, length);
} else {
fprintf(stderr, "ERROR: WDC: C2 log page not found with uuid index %d\n",
uuid_ix);
}
end:
free(data);
return valid;
}
static bool get_dev_mgmt_log_page_lid_data(struct nvme_dev *dev,
void **cbs_data,
__u8 lid,
@ -2371,6 +2650,78 @@ end:
return found;
}
static bool get_dev_mgment_data(nvme_root_t r, struct nvme_dev *dev,
void **data)
{
bool found = false;
*data = NULL;
__u32 device_id = 0, vendor_id = 0;
bool uuid_present = false;
int index = 0, uuid_index = 0;
struct nvme_id_uuid_list uuid_list;
/* The wdc_get_pci_ids function could fail when drives are connected
* via a PCIe switch. Therefore, the return code is intentionally
* being ignored. The device_id and vendor_id variables have been
* initialized to 0 so the code can continue on without issue for
* both cases: wdc_get_pci_ids successful or failed.
*/
wdc_get_pci_ids(r, dev, &device_id, &vendor_id);
typedef struct nvme_id_uuid_list_entry *uuid_list_entry;
memset(&uuid_list, 0, sizeof(struct nvme_id_uuid_list));
if (wdc_CheckUuidListSupport(dev, &uuid_list)) {
uuid_list_entry uuid_list_entry_ptr = (uuid_list_entry)&uuid_list.entry[0];
while (index <= NVME_ID_UUID_LIST_MAX &&
!wdc_UuidEqual(uuid_list_entry_ptr, (uuid_list_entry)UUID_END)) {
if (wdc_UuidEqual(uuid_list_entry_ptr,
(uuid_list_entry)WDC_UUID)) {
uuid_present = true;
break;
} else if (wdc_UuidEqual(uuid_list_entry_ptr,
(uuid_list_entry)WDC_UUID_SN640_3) &&
wdc_is_sn640_3(device_id)) {
uuid_present = true;
break;
}
index++;
uuid_list_entry_ptr = (uuid_list_entry)&uuid_list.entry[index];
}
if (uuid_present)
uuid_index = index + 1;
}
if (uuid_present) {
/* use the uuid index found above */
found = get_dev_mgmt_log_page_data(dev, data, uuid_index);
} else {
if (!uuid_index && needs_c2_log_page_check(device_id)) {
/* In certain devices that don't support UUID lists, there are multiple
* definitions of the C2 logpage. In those cases, the code
* needs to try two UUID indexes and use an identification algorithm
* to determine which is returning the correct log page data.
*/
uuid_index = 1;
}
found = get_dev_mgmt_log_page_data(dev, data, uuid_index);
if (!found) {
/* not found with uuid = 1 try with uuid = 0 */
uuid_index = 0;
fprintf(stderr, "Not found, requesting log page with uuid_index %d\n",
uuid_index);
found = get_dev_mgmt_log_page_data(dev, data, uuid_index);
}
}
return found;
}
static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev,
__u8 log_id, void **cbs_data)
{
@ -2485,6 +2836,22 @@ static bool wdc_nvme_check_supported_log_page(nvme_root_t r, struct nvme_dev *de
return found;
}
static bool wdc_nvme_parse_dev_status_log_entry(void *log_data, __u32 *ret_data,
__u32 entry_id)
{
struct wdc_c2_log_subpage_header *entry_data = NULL;
if (wdc_parse_dev_mng_log_entry(log_data, entry_id, &entry_data)) {
if (entry_data) {
*ret_data = le32_to_cpu(entry_data->data);
return true;
}
}
*ret_data = 0;
return false;
}
static bool wdc_nvme_get_dev_status_log_data(nvme_root_t r, struct nvme_dev *dev, __le32 *ret_data,
__u8 log_id)
{
@ -8609,12 +8976,13 @@ static int wdc_drive_status(int argc, char **argv, struct command *command,
struct nvme_dev *dev;
int ret = 0;
nvme_root_t r;
__le32 system_eol_state;
__le32 user_eol_state;
__le32 format_corrupt_reason = cpu_to_le32(0xFFFFFFFF);
__le32 eol_status;
__le32 assert_status = cpu_to_le32(0xFFFFFFFF);
__le32 thermal_status = cpu_to_le32(0xFFFFFFFF);
void *dev_mng_log = NULL;
__u32 system_eol_state;
__u32 user_eol_state;
__u32 format_corrupt_reason = 0xFFFFFFFF;
__u32 eol_status;
__u32 assert_status = 0xFFFFFFFF;
__u32 thermal_status = 0xFFFFFFFF;
__u64 capabilities = 0;
OPT_ARGS(opts) = {
@ -8641,35 +9009,41 @@ static int wdc_drive_status(int argc, char **argv, struct command *command,
goto out;
}
if (!get_dev_mgment_data(r, dev, &dev_mng_log)) {
fprintf(stderr, "ERROR: WDC: 0xC2 Log Page not found\n");
ret = -1;
goto out;
}
/* Get the assert dump present status */
if (!wdc_nvme_get_dev_status_log_data(r, dev, &assert_status,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &assert_status,
WDC_C2_ASSERT_DUMP_PRESENT_ID))
fprintf(stderr, "ERROR: WDC: Get Assert Status Failed\n");
/* Get the thermal throttling status */
if (!wdc_nvme_get_dev_status_log_data(r, dev, &thermal_status,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &thermal_status,
WDC_C2_THERMAL_THROTTLE_STATUS_ID))
fprintf(stderr, "ERROR: WDC: Get Thermal Throttling Status Failed\n");
/* Get EOL status */
if (!wdc_nvme_get_dev_status_log_data(r, dev, &eol_status,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &eol_status,
WDC_C2_USER_EOL_STATUS_ID)) {
fprintf(stderr, "ERROR: WDC: Get User EOL Status Failed\n");
eol_status = cpu_to_le32(-1);
}
/* Get Customer EOL state */
if (!wdc_nvme_get_dev_status_log_data(r, dev, &user_eol_state,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &user_eol_state,
WDC_C2_USER_EOL_STATE_ID))
fprintf(stderr, "ERROR: WDC: Get User EOL State Failed\n");
/* Get System EOL state*/
if (!wdc_nvme_get_dev_status_log_data(r, dev, &system_eol_state,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &system_eol_state,
WDC_C2_SYSTEM_EOL_STATE_ID))
fprintf(stderr, "ERROR: WDC: Get System EOL State Failed\n");
/* Get format corrupt reason*/
if (!wdc_nvme_get_dev_status_log_data(r, dev, &format_corrupt_reason,
if (!wdc_nvme_parse_dev_status_log_entry(dev_mng_log, &format_corrupt_reason,
WDC_C2_FORMAT_CORRUPT_REASON_ID))
fprintf(stderr, "ERROR: WDC: Get Format Corrupt Reason Failed\n");
@ -8716,6 +9090,7 @@ static int wdc_drive_status(int argc, char **argv, struct command *command,
else
printf(" Format Corrupt Reason: Unknown : 0x%08x\n", le32_to_cpu(format_corrupt_reason));
free(dev_mng_log);
out:
nvme_free_tree(r);
dev_close(dev);