From fa70d29099eaf10c4a91a749e56954264f04bf13 Mon Sep 17 00:00:00 2001 From: Brandon Paupore Date: Mon, 23 Jun 2025 14:24:33 -0500 Subject: [PATCH] sndk: vs-internal-log udui support This adds support for an updated version of the dui log retrieved over the vs-internal-log command, and imports the necessary pieces from the WDC plugin implementation to process the execution. With this change and the capabilities reworks committed earlier, this bumps the plugin version to 3.0.0. Reviewed-by: Jeffrey Lien Signed-off-by: Brandon Paupore --- plugins/sandisk/sandisk-nvme.c | 392 ++++++++++++++++++++++++++++++++ plugins/sandisk/sandisk-nvme.h | 2 +- plugins/sandisk/sandisk-utils.c | 101 +++++++- plugins/sandisk/sandisk-utils.h | 38 ++++ plugins/wdc/wdc-nvme.c | 1 + 5 files changed, 531 insertions(+), 3 deletions(-) diff --git a/plugins/sandisk/sandisk-nvme.c b/plugins/sandisk/sandisk-nvme.c index 2f33dffed4..06290ddd7b 100644 --- a/plugins/sandisk/sandisk-nvme.c +++ b/plugins/sandisk/sandisk-nvme.c @@ -28,12 +28,404 @@ #include "sandisk-utils.h" #include "plugins/wdc/wdc-nvme-cmds.h" +static int sndk_do_cap_telemetry_log(struct nvme_dev *dev, const char *file, + __u32 bs, int type, int data_area) +{ + struct nvme_telemetry_log *log; + size_t full_size = 0; + int err = 0, output; + __u32 host_gen = 1; + int ctrl_init = 0; + __u8 *data_ptr = NULL; + int data_written = 0, data_remaining = 0; + struct nvme_id_ctrl ctrl; + __u64 capabilities = 0; + nvme_root_t r; + + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) { + fprintf(stderr, "ERROR: WDC: nvme_identify_ctrl() failed 0x%x\n", err); + return err; + } + + if (!(ctrl.lpa & 0x8)) { + fprintf(stderr, "Telemetry log pages not supported by device\n"); + return -EINVAL; + } + + r = nvme_scan(NULL); + capabilities = sndk_get_drive_capabilities(r, dev); + + if (type == SNDK_TELEMETRY_TYPE_HOST) { + host_gen = 1; + ctrl_init = 0; + } else if (type == SNDK_TELEMETRY_TYPE_CONTROLLER) { + if (capabilities & SNDK_DRIVE_CAP_INTERNAL_LOG) { + err = sndk_check_ctrl_telemetry_option_disabled(dev); + if (err) + return err; + } + host_gen = 0; + ctrl_init = 1; + } else { + fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type); + return -EINVAL; + } + + if (!file) { + fprintf(stderr, "%s: Please provide an output file!\n", __func__); + return -EINVAL; + } + + output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "%s: Failed to open output file %s: %s!\n", + __func__, file, strerror(errno)); + return output; + } + + if (ctrl_init) + err = nvme_get_ctrl_telemetry(dev_fd(dev), true, &log, + data_area, &full_size); + else if (host_gen) + err = nvme_get_new_host_telemetry(dev_fd(dev), &log, + data_area, &full_size); + else + err = nvme_get_host_telemetry(dev_fd(dev), &log, data_area, + &full_size); + + if (err < 0) { + perror("get-telemetry-log"); + goto close_output; + } else if (err > 0) { + nvme_show_status(err); + fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__); + goto close_output; + } + + /* + *Continuously pull data until the offset hits the end of the last + *block. + */ + data_written = 0; + data_remaining = full_size; + data_ptr = (__u8 *)log; + + while (data_remaining) { + data_written = write(output, data_ptr, data_remaining); + + if (data_written < 0) { + data_remaining = data_written; + break; + } else if (data_written <= data_remaining) { + data_remaining -= data_written; + data_ptr += data_written; + } else { + /* Unexpected overwrite */ + fprintf(stderr, "Failure: Unexpected telemetry log overwrite\n" \ + "- data_remaining = 0x%x, data_written = 0x%x\n", + data_remaining, data_written); + break; + } + } + + if (fsync(output) < 0) { + fprintf(stderr, "ERROR: %s: fsync: %s\n", __func__, strerror(errno)); + err = -1; + } + + free(log); +close_output: + close(output); + return err; +} + +static __u32 sndk_dump_udui_data(int fd, __u32 dataLen, __u32 offset, + __u8 *dump_data) +{ + int ret; + struct nvme_passthru_cmd admin_cmd; + + memset(&admin_cmd, 0, sizeof(struct nvme_passthru_cmd)); + admin_cmd.opcode = SNDK_NVME_CAP_UDUI_OPCODE; + admin_cmd.nsid = 0xFFFFFFFF; + admin_cmd.addr = (__u64)(uintptr_t)dump_data; + admin_cmd.data_len = dataLen; + admin_cmd.cdw10 = ((dataLen >> 2) - 1); + admin_cmd.cdw12 = offset; + ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL); + if (ret) { + fprintf(stderr, "ERROR: SNDK: reading DUI data failed\n"); + nvme_show_status(ret); + } + + return ret; +} + +static int sndk_do_cap_udui(int fd, char *file, __u32 xfer_size, int verbose, + __u64 file_size, __u64 offset) +{ + int ret = 0; + int output; + ssize_t written = 0; + struct nvme_telemetry_log *log; + __u32 udui_log_hdr_size = sizeof(struct nvme_telemetry_log); + __u32 chunk_size = xfer_size; + __u64 total_size; + + log = (struct nvme_telemetry_log *)malloc(udui_log_hdr_size); + if (!log) { + fprintf(stderr, + "%s: ERROR: log header malloc failed : status %s, size 0x%x\n", + __func__, strerror(errno), udui_log_hdr_size); + return -1; + } + memset(log, 0, udui_log_hdr_size); + + /* get the udui telemetry and log headers */ + ret = sndk_dump_udui_data(fd, udui_log_hdr_size, 0, (__u8 *)log); + if (ret) { + fprintf(stderr, "%s: ERROR: SNDK: Get UDUI header failed\n", __func__); + nvme_show_status(ret); + goto out; + } + + total_size = (le32_to_cpu(log->dalb4) + 1) * 512; + if (offset > total_size) { + fprintf(stderr, "%s: ERROR: SNDK: offset larger than log length = 0x%"PRIx64"\n", + __func__, (uint64_t)total_size); + goto out; + } + + if (file_size && (total_size - offset) > file_size) + total_size = offset + file_size; + + log = (struct nvme_telemetry_log *)realloc(log, chunk_size); + + output = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (output < 0) { + fprintf(stderr, "%s: Failed to open output file %s: %s!\n", __func__, file, + strerror(errno)); + goto out; + } + + while (offset < total_size) { + if (chunk_size > total_size - offset) + chunk_size = total_size - offset; + ret = sndk_dump_udui_data(fd, chunk_size, offset, + ((__u8 *)log)); + if (ret) { + fprintf(stderr, + "%s: ERROR: Get UDUI failed, offset = 0x%"PRIx64", size = %u\n", + __func__, (uint64_t)offset, chunk_size); + break; + } + + /* write the dump data into the file */ + written = write(output, (void *)log, chunk_size); + if (written != chunk_size) { + fprintf(stderr, + "%s: ERROR: SNDK: Failed to flush DUI data to file!\n" \ + "- written = %zd, offset = 0x%"PRIx64", chunk_size = %u\n", + __func__, written, (uint64_t)offset, chunk_size); + ret = errno; + break; + } + + offset += chunk_size; + } + + close(output); + nvme_show_status(ret); + if (verbose) + fprintf(stderr, + "INFO: SNDK: Capture Device Unit Info log length = 0x%"PRIx64"\n", + (uint64_t)total_size); + +out: + free(log); + return ret; +} static int sndk_vs_internal_fw_log(int argc, char **argv, struct command *command, struct plugin *plugin) { + const char *desc = "Internal Firmware Log."; + const char *file = "Output file pathname."; + const char *size = "Data retrieval transfer size."; + const char *data_area = + "Data area to retrieve up to. Currently only supported on the SN340, SN640, SN730, and SN840 devices."; + const char *file_size = "Output file size. Currently only supported on the SN340 device."; + const char *offset = + "Output file data offset. Currently only supported on the SN340 device."; + const char *type = + "Telemetry type - NONE, HOST, or CONTROLLER Currently only supported on the SN530, SN640, SN730, SN740, SN810, SN840 and ZN350 devices."; + const char *verbose = "Display more debug messages."; + char f[PATH_MAX] = {0}; + char fileSuffix[PATH_MAX] = {0}; + struct nvme_dev *dev; + nvme_root_t r; + __u32 xfer_size = 0; + int telemetry_type = 0, telemetry_data_area = 0; + struct SNDK_UtilsTimeInfo timeInfo; + __u8 timeStamp[SNDK_MAX_PATH_LEN]; + __u64 capabilities = 0; + __u32 device_id, read_vendor_id; + int ret = -1; + + struct config { + char *file; + __u32 xfer_size; + int data_area; + __u64 file_size; + __u64 offset; + char *type; + bool verbose; + }; + + struct config cfg = { + .file = NULL, + .xfer_size = 0x10000, + .data_area = 0, + .file_size = 0, + .offset = 0, + .type = NULL, + .verbose = false, + }; + + OPT_ARGS(opts) = { + OPT_FILE("output-file", 'o', &cfg.file, file), + OPT_UINT("transfer-size", 's', &cfg.xfer_size, size), + OPT_UINT("data-area", 'd', &cfg.data_area, data_area), + OPT_LONG("file-size", 'f', &cfg.file_size, file_size), + OPT_LONG("offset", 'e', &cfg.offset, offset), + OPT_FILE("type", 't', &cfg.type, type), + OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), + OPT_END() + }; + + ret = parse_and_open(&dev, argc, argv, desc, opts); + if (ret) + return ret; + + r = nvme_scan(NULL); + if (!sndk_check_device(r, dev)) + goto out; + + if (cfg.xfer_size) { + xfer_size = cfg.xfer_size; + } else { + fprintf(stderr, "ERROR: SNDK: Invalid length\n"); + goto out; + } + + ret = sndk_get_pci_ids(r, dev, &device_id, &read_vendor_id); + + if (cfg.file) { + int verify_file; + + /* verify file name and path is valid before getting dump data */ + verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (verify_file < 0) { + fprintf(stderr, "ERROR: SNDK: open: %s\n", strerror(errno)); + goto out; + } + close(verify_file); + strncpy(f, cfg.file, PATH_MAX - 1); + } else { + sndk_UtilsGetTime(&timeInfo); + memset(timeStamp, 0, sizeof(timeStamp)); + sndk_UtilsSnprintf((char *)timeStamp, SNDK_MAX_PATH_LEN, + "%02u%02u%02u_%02u%02u%02u", timeInfo.year, + timeInfo.month, timeInfo.dayOfMonth, + timeInfo.hour, timeInfo.minute, + timeInfo.second); + snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char *)timeStamp); + + ret = sndk_get_serial_name(dev, f, PATH_MAX, fileSuffix); + if (ret) { + fprintf(stderr, "ERROR: SNDK: failed to generate file name\n"); + goto out; + } + } + + if (!cfg.file) { + if (strlen(f) > PATH_MAX - 5) { + fprintf(stderr, "ERROR: SNDK: file name overflow\n"); + ret = -1; + goto out; + } + strcat(f, ".bin"); + } + fprintf(stderr, "%s: filename = %s\n", __func__, f); + + if (cfg.data_area) { + if (cfg.data_area > 5 || cfg.data_area < 1) { + fprintf(stderr, "ERROR: SNDK: Data area must be 1-5\n"); + ret = -1; + goto out; + } + } + + if (!cfg.type || !strcmp(cfg.type, "NONE") || !strcmp(cfg.type, "none")) { + telemetry_type = SNDK_TELEMETRY_TYPE_NONE; + data_area = 0; + } else if (!strcmp(cfg.type, "HOST") || !strcmp(cfg.type, "host")) { + telemetry_type = SNDK_TELEMETRY_TYPE_HOST; + telemetry_data_area = cfg.data_area; + } else if (!strcmp(cfg.type, "CONTROLLER") || !strcmp(cfg.type, "controller")) { + telemetry_type = SNDK_TELEMETRY_TYPE_CONTROLLER; + telemetry_data_area = cfg.data_area; + } else { + fprintf(stderr, + "ERROR: SNDK: Invalid type - Must be NONE, HOST or CONTROLLER\n"); + ret = -1; + goto out; + } + + capabilities = sndk_get_drive_capabilities(r, dev); + + /* Supported through WDC plugin for non-telemetry */ + if ((capabilities & SNDK_DRIVE_CAP_INTERNAL_LOG) && + (telemetry_type != SNDK_TELEMETRY_TYPE_NONE)) { + /* Set the default DA to 3 if not specified */ + if (!telemetry_data_area) + telemetry_data_area = 3; + + ret = sndk_do_cap_telemetry_log(dev, f, xfer_size, + telemetry_type, telemetry_data_area); + goto out; + } + + if (capabilities & SNDK_DRIVE_CAP_UDUI) { + if ((telemetry_type == SNDK_TELEMETRY_TYPE_HOST) || + (telemetry_type == SNDK_TELEMETRY_TYPE_CONTROLLER)) { + /* Set the default DA to 3 if not specified */ + if (!telemetry_data_area) + telemetry_data_area = 3; + + ret = sndk_do_cap_telemetry_log(dev, f, xfer_size, + telemetry_type, telemetry_data_area); + goto out; + } else { + ret = sndk_do_cap_udui(dev_fd(dev), f, xfer_size, + cfg.verbose, cfg.file_size, + cfg.offset); + goto out; + } + } + + /* Fallback to WDC plugin if otherwise not supported */ + nvme_free_tree(r); + dev_close(dev); return run_wdc_vs_internal_fw_log(argc, argv, command, plugin); + +out: + nvme_free_tree(r); + dev_close(dev); + return ret; } static int sndk_vs_nand_stats(int argc, char **argv, diff --git a/plugins/sandisk/sandisk-nvme.h b/plugins/sandisk/sandisk-nvme.h index 037c895c4a..af22ff1366 100644 --- a/plugins/sandisk/sandisk-nvme.h +++ b/plugins/sandisk/sandisk-nvme.h @@ -5,7 +5,7 @@ #if !defined(SANDISK_NVME) || defined(CMD_HEADER_MULTI_READ) #define SANDISK_NVME -#define SANDISK_PLUGIN_VERSION "2.14.1" +#define SANDISK_PLUGIN_VERSION "3.0.0" #include "cmd.h" PLUGIN(NAME("sndk", "Sandisk vendor specific extensions", SANDISK_PLUGIN_VERSION), diff --git a/plugins/sandisk/sandisk-utils.c b/plugins/sandisk/sandisk-utils.c index a741264361..113c97f9b5 100644 --- a/plugins/sandisk/sandisk-utils.c +++ b/plugins/sandisk/sandisk-utils.c @@ -163,7 +163,7 @@ __u64 sndk_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) SNDK_DRIVE_CAP_CA_LOG_PAGE | SNDK_DRIVE_CAP_OCP_C4_LOG_PAGE | SNDK_DRIVE_CAP_OCP_C5_LOG_PAGE | - SNDK_DRIVE_CAP_DUI | + SNDK_DRIVE_CAP_UDUI | SNDK_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 | SNDK_DRIVE_CAP_VU_FID_CLEAR_PCIE | SNDK_DRIVE_CAP_VU_FID_CLEAR_FW_ACT_HISTORY | @@ -186,7 +186,7 @@ __u64 sndk_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev) case SNDK_NVME_SN7150_DEV_ID_3: case SNDK_NVME_SN7150_DEV_ID_4: case SNDK_NVME_SN7150_DEV_ID_5: - capabilities = SNDK_DRIVE_CAP_DUI; + capabilities = SNDK_DRIVE_CAP_UDUI; break; default: @@ -267,4 +267,101 @@ __u64 sndk_get_enc_drive_capabilities(nvme_root_t r, return capabilities; } +int sndk_get_serial_name(struct nvme_dev *dev, char *file, size_t len, + const char *suffix) +{ + int i; + int ret; + int res_len = 0; + char orig[PATH_MAX] = {0}; + struct nvme_id_ctrl ctrl; + int ctrl_sn_len = sizeof(ctrl.sn); + i = sizeof(ctrl.sn) - 1; + strncpy(orig, file, PATH_MAX - 1); + memset(file, 0, len); + memset(&ctrl, 0, sizeof(struct nvme_id_ctrl)); + ret = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (ret) { + fprintf(stderr, "ERROR: SNDK: nvme_identify_ctrl() failed 0x%x\n", ret); + return -1; + } + /* Remove trailing spaces from the name */ + while (i && ctrl.sn[i] == ' ') { + ctrl.sn[i] = '\0'; + i--; + } + if (ctrl.sn[sizeof(ctrl.sn) - 1] == '\0') + ctrl_sn_len = strlen(ctrl.sn); + + res_len = snprintf(file, len, "%s%.*s%s", orig, ctrl_sn_len, ctrl.sn, suffix); + if (len <= res_len) { + fprintf(stderr, + "ERROR: SNDK: cannot format SN due to unexpected length\n"); + return -1; + } + + return 0; +} + +void sndk_UtilsGetTime(struct SNDK_UtilsTimeInfo *timeInfo) +{ + time_t currTime; + struct tm currTimeInfo; + + tzset(); + time(&currTime); + localtime_r(&currTime, &currTimeInfo); + + timeInfo->year = currTimeInfo.tm_year + 1900; + timeInfo->month = currTimeInfo.tm_mon + 1; + timeInfo->dayOfWeek = currTimeInfo.tm_wday; + timeInfo->dayOfMonth = currTimeInfo.tm_mday; + timeInfo->hour = currTimeInfo.tm_hour; + timeInfo->minute = currTimeInfo.tm_min; + timeInfo->second = currTimeInfo.tm_sec; + timeInfo->msecs = 0; + timeInfo->isDST = currTimeInfo.tm_isdst; +#ifdef HAVE_TM_GMTOFF + timeInfo->zone = -currTimeInfo.tm_gmtoff / 60; +#else /* HAVE_TM_GMTOFF */ + timeInfo->zone = -1 * (timezone / 60); +#endif /* HAVE_TM_GMTOFF */ +} + +int sndk_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...) +{ + int res = 0; + va_list vArgs; + + va_start(vArgs, format); + res = vsnprintf(buffer, sizeOfBuffer, format, vArgs); + va_end(vArgs); + + return res; +} + +/* Verify the Controller Initiated Option is enabled */ +int sndk_check_ctrl_telemetry_option_disabled(struct nvme_dev *dev) +{ + int err; + __u32 result; + + err = nvme_get_features_data(dev_fd(dev), + SNDK_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, + 0, 4, NULL, &result); + if (!err) { + if (result) { + fprintf(stderr, + "%s: Controller-initiated option telemetry disabled\n", + __func__); + return -EINVAL; + } + } else { + fprintf(stderr, "ERROR: SNDK: Get telemetry option feature failed."); + nvme_show_status(err); + return -EPERM; + } + + return 0; +} diff --git a/plugins/sandisk/sandisk-utils.h b/plugins/sandisk/sandisk-utils.h index 3324c1e5bd..2f55815220 100644 --- a/plugins/sandisk/sandisk-utils.h +++ b/plugins/sandisk/sandisk-utils.h @@ -139,6 +139,7 @@ #define SNDK_DRIVE_CAP_OCP_C5_LOG_PAGE 0x0000008000000000 #define SNDK_DRIVE_CAP_DEVICE_WAF 0x0000010000000000 #define SNDK_DRIVE_CAP_SET_LATENCY_MONITOR 0x0000020000000000 +#define SNDK_DRIVE_CAP_UDUI 0x0000040000000000 /* Any new capability flags should be added to the WDC plugin */ #define SNDK_DRIVE_CAP_SMART_LOG_MASK (SNDK_DRIVE_CAP_C0_LOG_PAGE | \ @@ -150,6 +151,7 @@ SNDK_DRIVE_CAP_VU_FID_CLEAR_PCIE) #define SNDK_DRIVE_CAP_INTERNAL_LOG_MASK (SNDK_DRIVE_CAP_INTERNAL_LOG | \ SNDK_DRIVE_CAP_DUI | \ + SNDK_DRIVE_CAP_UDUI | \ SNDK_DRIVE_CAP_DUI_DATA | \ SNDK_DRIVE_CAP_VUC_LOG) #define SNDK_DRIVE_CAP_FW_ACTIVATE_HISTORY_MASK (SNDK_DRIVE_CAP_FW_ACTIVATE_HISTORY | \ @@ -170,6 +172,9 @@ #define SNDK_NVME_GET_FW_ACT_HISTORY_LOG_ID 0xCB #define SNDK_NVME_GET_VU_SMART_LOG_ID 0xD0 +/* Vendor defined Feature IDs */ +#define SNDK_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID 0xD2 + /* Customer ID's */ #define SNDK_CUSTOMER_ID_GN 0x0001 #define SNDK_CUSTOMER_ID_GD 0x0101 @@ -181,6 +186,30 @@ #define SNDK_CUSTOMER_ID_0x1304 0x1304 #define SNDK_INVALID_CUSTOMER_ID -1 +/* Capture Device Unit Info */ +#define SNDK_NVME_CAP_UDUI_OPCODE 0xFA + +/* Telemtery types for vs-internal-log command */ +#define SNDK_TELEMETRY_TYPE_NONE 0x0 +#define SNDK_TELEMETRY_TYPE_HOST 0x1 +#define SNDK_TELEMETRY_TYPE_CONTROLLER 0x2 + +/* Misc */ +#define SNDK_MAX_PATH_LEN 256 + +struct SNDK_UtilsTimeInfo { + unsigned int year; + unsigned int month; + unsigned int dayOfWeek; + unsigned int dayOfMonth; + unsigned int hour; + unsigned int minute; + unsigned int second; + unsigned int msecs; + unsigned char isDST; /*0 or 1 */ + int zone; /* Zone value like +530 or -300 */ +}; + int sndk_get_pci_ids(nvme_root_t r, struct nvme_dev *dev, uint32_t *device_id, @@ -198,3 +227,12 @@ __u64 sndk_get_drive_capabilities(nvme_root_t r, __u64 sndk_get_enc_drive_capabilities(nvme_root_t r, struct nvme_dev *dev); +int sndk_get_serial_name(struct nvme_dev *dev, char *file, size_t len, + const char *suffix); + +void sndk_UtilsGetTime(struct SNDK_UtilsTimeInfo *timeInfo); + +int sndk_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, + const char *format, ...); + +int sndk_check_ctrl_telemetry_option_disabled(struct nvme_dev *dev); diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 1dd77e9a9b..03564808fb 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -189,6 +189,7 @@ #define WDC_DRIVE_CAP_OCP_C5_LOG_PAGE 0x0000008000000000 #define WDC_DRIVE_CAP_DEVICE_WAF 0x0000010000000000 #define WDC_DRIVE_CAP_SET_LATENCY_MONITOR 0x0000020000000000 +#define WDC_DRIVE_CAP_RESERVED1 0x0000040000000000 /* Any new capability flags should be added to the SNDK plugin */ #define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C0_LOG_PAGE | \