diff --git a/src/cli/features/core/ValidationFeature.cpp b/src/cli/features/core/ValidationFeature.cpp index 3082fed..d577dc4 100644 --- a/src/cli/features/core/ValidationFeature.cpp +++ b/src/cli/features/core/ValidationFeature.cpp @@ -93,6 +93,8 @@ void cli::nvmcli::ValidationFeature::getPaths(cli::framework::CommandSpecList &l injectError.addProperty(FATAL_MEDIA_ERROR_PROPERTYNAME, false, "1", true, TR("Inject a fake media fatal error which will cause the firmware to generate an error log " "and an alert.")); + injectError.addProperty(DIRTY_SHUTDOWN_PROPERTYNAME, false, "1", true, + TR("Inject an ADR failure resulting in dirty shutdown upon reboot.")); list.push_back(injectError); } @@ -101,7 +103,7 @@ void cli::nvmcli::ValidationFeature::getPaths(cli::framework::CommandSpecList &l cli::nvmcli::ValidationFeature::ValidationFeature() : cli::nvmcli::VerboseFeatureBase(), m_dimmUid(""), m_poisontype(POISON_MEMORY_TYPE_PATROLSCRUB), m_temperature(0), m_poison(0), m_clearStateExists(false), m_temperatureExists(false), m_poisonExists(false), m_poisonTypeExists(false), m_dieSparingExists(false), - m_spareAlarmExists(false), m_fatalMediaErrorExists(false) + m_spareAlarmExists(false), m_fatalMediaErrorExists(false), m_dirtyShutdownExists(false) { } /* @@ -170,6 +172,14 @@ void cli::nvmcli::ValidationFeature::inject_error(std::string &prefixMsg, m_DimmProvider.injectSoftwareTrigger(*iUid, ERROR_TYPE_MEDIA_FATAL_ERROR); listResult.insert(prefixMsg + cli::framework::SUCCESS_MSG); } + else if (m_dirtyShutdownExists) + { + prefixMsg = framework::ResultBase::stringFromArgList( + SETDIRTYSHUTDOWN_MSG_PREFIX.c_str(), m_dimmUid.c_str()); + prefixMsg += ": "; + m_DimmProvider.injectSoftwareTrigger(*iUid, ERROR_TYPE_DIRTY_SHUTDOWN); + listResult.insert(prefixMsg + cli::framework::SUCCESS_MSG); + } } void cli::nvmcli::ValidationFeature::clear_injected_error(std::string &prefixMsg, @@ -199,6 +209,14 @@ void cli::nvmcli::ValidationFeature::clear_injected_error(std::string &prefixMsg m_DimmProvider.clearSoftwareTrigger(*iUid, ERROR_TYPE_DIE_SPARING); listResult.insert(prefixMsg + cli::framework::SUCCESS_MSG); } + else if (m_dirtyShutdownExists) + { + prefixMsg = framework::ResultBase::stringFromArgList( + CLEARDIRTYSHUTDOWN_MSG_PREFIX.c_str(), m_dimmUid.c_str()); + prefixMsg += ": "; + m_DimmProvider.clearSoftwareTrigger(*iUid, ERROR_TYPE_DIRTY_SHUTDOWN); + listResult.insert(prefixMsg + cli::framework::SUCCESS_MSG); + } } /* @@ -433,6 +451,23 @@ cli::framework::ResultBase* cli::nvmcli::ValidationFeature::parseFatalMediaError return pResult; } +cli::framework::ResultBase* cli::nvmcli::ValidationFeature::parseDirtyShutdownProperty( + const framework::ParsedCommand& parsedCommand) +{ + LogEnterExit logging(__FUNCTION__, __FILE__, __LINE__); + framework::ResultBase *pResult = NULL; + + std::string propValue = + framework::Parser::getPropertyValue(parsedCommand, DIRTY_SHUTDOWN_PROPERTYNAME, &m_dirtyShutdownExists); + if (m_dirtyShutdownExists) + { + pResult = verifySWTriggerPropertyValue(propValue, DIRTY_SHUTDOWN_PROPERTYNAME); + } + + return pResult; +} + + cli::framework::ResultBase* cli::nvmcli::ValidationFeature::getInjectErrorAttributes( const framework::ParsedCommand& parsedCommand) { @@ -464,6 +499,10 @@ cli::framework::ResultBase* cli::nvmcli::ValidationFeature::getInjectErrorAttrib { pResult = parseFatalMediaErrorProperty(parsedCommand); } + if (!pResult) + { + pResult = parseDirtyShutdownProperty(parsedCommand); + } if (!pResult) { @@ -499,6 +538,10 @@ cli::framework::ResultBase* cli::nvmcli::ValidationFeature::errorIfMoreThanOnePr { list.push_back(FATAL_MEDIA_ERROR_PROPERTYNAME); } + if (m_dirtyShutdownExists) + { + list.push_back(DIRTY_SHUTDOWN_PROPERTYNAME); + } if (list.size() > 1) { diff --git a/src/cli/features/core/ValidationFeature.h b/src/cli/features/core/ValidationFeature.h index cf9806a..f5c1232 100644 --- a/src/cli/features/core/ValidationFeature.h +++ b/src/cli/features/core/ValidationFeature.h @@ -47,12 +47,14 @@ static const std::string SETPOISON_MSG_PREFIX = N_TR("Poison address %llu on " N static const std::string SETDIESPARING_MSG_PREFIX = N_TR("Trigger die sparing on " NVM_DIMM_NAME " %s"); static const std::string SETSPARECAPACITYALARM_MSG_PREFIX = N_TR("Trigger a spare capacity on " NVM_DIMM_NAME " %s"); static const std::string SETFATALERROR_MSG_PREFIX = N_TR("Create a media fatal error on " NVM_DIMM_NAME " %s"); +static const std::string SETDIRTYSHUTDOWN_MSG_PREFIX = N_TR("Set dirty shutdown on " NVM_DIMM_NAME " %s"); static const std::string CLEARPOISON_MSG_PREFIX = N_TR("Clear poison of address %llu on " NVM_DIMM_NAME " %s"); static const std::string CLEARTEMPERATURE_MSG_PREFIX = N_TR("Clear injected temperature on " NVM_DIMM_NAME " %s"); static const std::string CLEARDIESPARING_MSG_PREFIX = N_TR("Clear injected die sparing on " NVM_DIMM_NAME " %s"); static const std::string CLEARSPARECAPACITYALARM_MSG_PREFIX = N_TR("Clear injected spare capacity alarm on " NVM_DIMM_NAME " %s"); static const std::string CLEARFATALERROR_MSG_PREFIX = N_TR("Clear injected media fatal error on " NVM_DIMM_NAME " %s"); +static const std::string CLEARDIRTYSHUTDOWN_MSG_PREFIX = N_TR("Clear dirty shutdown on " NVM_DIMM_NAME " %s"); static std::string CLEAR_PROPERTYNAME = "Clear"; static std::string TEMPERATURE_PROPERTYNAME = "Temperature"; @@ -61,6 +63,7 @@ static std::string POISON_MEMORY_TYPE_PROPERTYNAME = "PoisonType"; static std::string DIE_SPARING_PROPERTYNAME = "DieSparing"; static std::string SPARE_ALARM_PROPERTYNAME = "SpareAlarm"; static std::string FATAL_MEDIA_ERROR_PROPERTYNAME = "FatalMediaError"; +static std::string DIRTY_SHUTDOWN_PROPERTYNAME = "DirtyShutdown"; // memory type to poison strings static std::string MEMORY_TYPE_STR_MEMORYMODE = "MemoryMode"; @@ -116,6 +119,7 @@ class NVM_API ValidationFeature : public cli::nvmcli::VerboseFeatureBase bool m_dieSparingExists; bool m_spareAlarmExists; bool m_fatalMediaErrorExists; + bool m_dirtyShutdownExists; /* * Helper for inject error. @@ -153,6 +157,9 @@ class NVM_API ValidationFeature : public cli::nvmcli::VerboseFeatureBase cli::framework::ResultBase* parseFatalMediaErrorProperty( const framework::ParsedCommand& parsedCommand); + cli::framework::ResultBase* parseDirtyShutdownProperty( + const framework::ParsedCommand& parsedCommand); + cli::framework::ResultBase* verifyPropertyCount( const framework::ParsedCommand& parsedCommand); diff --git a/src/common/persistence/schema.c b/src/common/persistence/schema.c index fe5367a..130fb90 100644 --- a/src/common/persistence/schema.c +++ b/src/common/persistence/schema.c @@ -1927,7 +1927,8 @@ tables[populate_index++] = ((struct table){"software_trigger_info", device_handle INTEGER PRIMARY KEY NOT NULL UNIQUE , \ die_sparing_trigger INTEGER , \ user_spare_block_alarm_trip_trigger INTEGER , \ - fatal_error_trigger INTEGER \ + fatal_error_trigger INTEGER , \ + dirty_shutdown_trigger INTEGER \ );"}); tables[populate_index++] = ((struct table){"software_trigger_info_history", "CREATE TABLE software_trigger_info_history ( \ @@ -1935,7 +1936,8 @@ tables[populate_index++] = ((struct table){"software_trigger_info", device_handle INTEGER , \ die_sparing_trigger INTEGER , \ user_spare_block_alarm_trip_trigger INTEGER , \ - fatal_error_trigger INTEGER \ + fatal_error_trigger INTEGER , \ + dirty_shutdown_trigger INTEGER \ );"}); tables[populate_index++] = ((struct table){"performance", "CREATE TABLE performance ( \ @@ -33127,6 +33129,7 @@ void local_bind_software_trigger_info(sqlite3_stmt *p_stmt, struct db_software_t BIND_INTEGER(p_stmt, "$die_sparing_trigger", (unsigned int)p_software_trigger_info->die_sparing_trigger); BIND_INTEGER(p_stmt, "$user_spare_block_alarm_trip_trigger", (unsigned int)p_software_trigger_info->user_spare_block_alarm_trip_trigger); BIND_INTEGER(p_stmt, "$fatal_error_trigger", (unsigned int)p_software_trigger_info->fatal_error_trigger); + BIND_INTEGER(p_stmt, "$dirty_shutdown_trigger", (unsigned int)p_software_trigger_info->dirty_shutdown_trigger); } void local_get_software_trigger_info_relationships(const PersistentStore *p_ps, sqlite3_stmt *p_stmt, struct db_software_trigger_info *p_software_trigger_info) @@ -33154,6 +33157,9 @@ void local_row_to_software_trigger_info(const PersistentStore *p_ps, INTEGER_COLUMN(p_stmt, 3, p_software_trigger_info->fatal_error_trigger); + INTEGER_COLUMN(p_stmt, + 4, + p_software_trigger_info->dirty_shutdown_trigger); } void db_print_software_trigger_info(struct db_software_trigger_info *p_value) { @@ -33161,6 +33167,7 @@ void db_print_software_trigger_info(struct db_software_trigger_info *p_value) printf("software_trigger_info.die_sparing_trigger: %u\n", p_value->die_sparing_trigger); printf("software_trigger_info.user_spare_block_alarm_trip_trigger: %u\n", p_value->user_spare_block_alarm_trip_trigger); printf("software_trigger_info.fatal_error_trigger: %u\n", p_value->fatal_error_trigger); + printf("software_trigger_info.dirty_shutdown_trigger: %u\n", p_value->dirty_shutdown_trigger); } enum db_return_codes db_add_software_trigger_info(const PersistentStore *p_ps, struct db_software_trigger_info *p_software_trigger_info) @@ -33168,12 +33175,13 @@ enum db_return_codes db_add_software_trigger_info(const PersistentStore *p_ps, enum db_return_codes rc = DB_ERR_FAILURE; sqlite3_stmt *p_stmt; char *sql = "INSERT INTO software_trigger_info \ - (device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger) \ + (device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger, dirty_shutdown_trigger) \ VALUES \ ($device_handle, \ $die_sparing_trigger, \ $user_spare_block_alarm_trip_trigger, \ - $fatal_error_trigger) "; + $fatal_error_trigger, \ + $dirty_shutdown_trigger) "; int sql_rc; if ((sql_rc = SQLITE_PREPARE(p_ps->db, sql, p_stmt)) == SQLITE_OK) { @@ -33211,9 +33219,10 @@ int db_get_software_trigger_infos(const PersistentStore *p_ps, , die_sparing_trigger \ , user_spare_block_alarm_trip_trigger \ , fatal_error_trigger \ + , dirty_shutdown_trigger \ \ FROM software_trigger_info \ - \ + \ \ "; sqlite3_stmt *p_stmt; @@ -33265,12 +33274,13 @@ enum db_return_codes db_save_software_trigger_info_state(const PersistentStore * { sqlite3_stmt *p_stmt; char *sql = "INSERT INTO software_trigger_info \ - ( device_handle , die_sparing_trigger , user_spare_block_alarm_trip_trigger , fatal_error_trigger ) \ + ( device_handle , die_sparing_trigger , user_spare_block_alarm_trip_trigger , fatal_error_trigger , dirty_shutdown_trigger ) \ VALUES \ ($device_handle, \ $die_sparing_trigger, \ $user_spare_block_alarm_trip_trigger, \ - $fatal_error_trigger) "; + $fatal_error_trigger, \ + $dirty_shutdown_trigger) "; int sql_rc; if ((sql_rc = SQLITE_PREPARE(p_ps->db, sql, p_stmt)) == SQLITE_OK) { @@ -33297,12 +33307,13 @@ enum db_return_codes db_save_software_trigger_info_state(const PersistentStore * sqlite3_stmt *p_stmt; char *sql = "INSERT INTO software_trigger_info_history \ (history_id, \ - device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger) \ + device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger, dirty_shutdown_trigger) \ VALUES ($history_id, \ $device_handle , \ $die_sparing_trigger , \ $user_spare_block_alarm_trip_trigger , \ - $fatal_error_trigger )"; + $fatal_error_trigger , \ + $dirty_shutdown_trigger )"; int sql_rc; if ((sql_rc = SQLITE_PREPARE(p_ps->db, sql, p_stmt)) == SQLITE_OK) { @@ -33338,7 +33349,7 @@ enum db_return_codes db_get_software_trigger_info_by_device_handle(const Persist enum db_return_codes rc = DB_ERR_FAILURE; sqlite3_stmt *p_stmt; char *sql = "SELECT \ - device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger \ + device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger, dirty_shutdown_trigger \ FROM software_trigger_info \ WHERE device_handle = $device_handle"; int sql_rc; @@ -33379,6 +33390,7 @@ enum db_return_codes db_update_software_trigger_info_by_device_handle(const Pers , die_sparing_trigger=$die_sparing_trigger \ , user_spare_block_alarm_trip_trigger=$user_spare_block_alarm_trip_trigger \ , fatal_error_trigger=$fatal_error_trigger \ + , dirty_shutdown_trigger=$dirty_shutdown_trigger \ \ WHERE device_handle=$device_handle "; int sql_rc; @@ -33503,7 +33515,7 @@ int db_get_software_trigger_info_history_by_history_id(const PersistentStore *p_ memset(p_software_trigger_info, 0, sizeof (struct db_software_trigger_info) * software_trigger_info_count); sqlite3_stmt *p_stmt; char *sql = "SELECT \ - device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger \ + device_handle, die_sparing_trigger, user_spare_block_alarm_trip_trigger, fatal_error_trigger, dirty_shutdown_trigger \ FROM software_trigger_info_history WHERE history_id = $history_id"; int sql_rc; if ((sql_rc = SQLITE_PREPARE(p_ps->db, sql, p_stmt)) == SQLITE_OK) diff --git a/src/common/persistence/schema.h b/src/common/persistence/schema.h index b356a2b..b706ebd 100644 --- a/src/common/persistence/schema.h +++ b/src/common/persistence/schema.h @@ -11107,6 +11107,7 @@ struct db_software_trigger_info unsigned int die_sparing_trigger; unsigned int user_spare_block_alarm_trip_trigger; unsigned int fatal_error_trigger; + unsigned int dirty_shutdown_trigger; }; /*! * Helper function to print a db_software_trigger_info to the screen. diff --git a/src/common/persistence/schema.txt b/src/common/persistence/schema.txt index 0c2ffb0..519b155 100644 --- a/src/common/persistence/schema.txt +++ b/src/common/persistence/schema.txt @@ -724,6 +724,7 @@ Attributes: unsigned int die_sparing_trigger unsigned int user_spare_block_alarm_trip_trigger unsigned int fatal_error_trigger + unsigned int dirty_shutdown_trigger Table(s): db_performance Description: Monitor stored DIMM performance metrics. diff --git a/src/lib/error_injection.c b/src/lib/error_injection.c index f70e2ee..55ca52f 100644 --- a/src/lib/error_injection.c +++ b/src/lib/error_injection.c @@ -42,6 +42,7 @@ int inject_temperature_error(NVM_UINT32 device_handle, NVM_UINT64 temperature, NVM_BOOL enable_injection); int inject_software_trigger(struct device_discovery *p_discovery, enum error_type type, NVM_BOOL enable_trigger); +int inject_dirty_shutdown_trigger(struct device_discovery * p_discovery, NVM_BOOL enable_trigger); /* * Helper function to enable/disable software trigger @@ -131,6 +132,37 @@ int inject_temperature_error(NVM_UINT32 device_handle, NVM_UINT64 temperature, return rc; } +/* + * Helper function to inject dirty shutdown trigger + */ +int inject_dirty_shutdown_trigger(struct device_discovery *p_discovery, NVM_BOOL enable_trigger) +{ + COMMON_LOG_ENTRY(); + int rc = NVM_SUCCESS; + + // Set up input payload + struct pt_payload_sw_triggers input; + memset(&input, 0, sizeof (input)); + input.triggers_to_modify = 0x1 << 4; // unsafe shutdown trigger + input.unsafe_shutdown_trigger = enable_trigger; + + struct fw_cmd cmd; + memset(&cmd, 0, sizeof (cmd)); + cmd.device_handle = p_discovery->device_handle.handle; + cmd.opcode = PT_INJECT_ERROR; + cmd.sub_opcode = SUBOP_ERROR_SW_TRIGGERS; + cmd.input_payload = &input; + cmd.input_payload_size = sizeof (input); + rc = ioctl_passthrough_cmd(&cmd); + if (rc != NVM_SUCCESS) + { + COMMON_LOG_ERROR_F("Failed to trigger dirty shutdown trip on dimm %u", cmd.device_handle); + } + + COMMON_LOG_EXIT_RETURN_I(rc); + return rc; +} + int verify_dimm_lock_state(enum lock_state lock_state) { int rc = NVM_SUCCESS; @@ -235,6 +267,9 @@ int nvm_inject_device_error(const NVM_UID device_uid, case ERROR_TYPE_POISON: rc = inject_poison_error(&discovery, p_error->dpa, p_error->memory_type, 1); break; + case ERROR_TYPE_DIRTY_SHUTDOWN: + rc = inject_dirty_shutdown_trigger(&discovery, 1); + break; case ERROR_TYPE_DIE_SPARING: case ERROR_TYPE_SPARE_ALARM: case ERROR_TYPE_MEDIA_FATAL_ERROR: @@ -292,6 +327,9 @@ int nvm_clear_injected_device_error(const NVM_UID device_uid, case ERROR_TYPE_POISON: rc = inject_poison_error(&discovery, p_error->dpa, p_error->memory_type, 0); break; + case ERROR_TYPE_DIRTY_SHUTDOWN: + rc = inject_dirty_shutdown_trigger(&discovery, 0); + break; case ERROR_TYPE_DIE_SPARING: rc = inject_software_trigger(&discovery, p_error->type, 0); break; diff --git a/src/lib/fis_types.h b/src/lib/fis_types.h index e5b96c3..2c05ba9 100644 --- a/src/lib/fis_types.h +++ b/src/lib/fis_types.h @@ -1390,7 +1390,9 @@ struct pt_payload_sw_triggers { * Bit 0: Die Spare Trigger * Bit 1: Used Spare Block Alarm Trip Trigger * Bit 2: Fatal Error Trigger - * Bit 63-3: Reserved + * Bit 3: Spare Block Percentage Trigger + * Bit 4: Unsafe Shutdown Trigger + * Bit 63-5: Reserved */ unsigned long long triggers_to_modify; @@ -1417,7 +1419,23 @@ struct pt_payload_sw_triggers { */ unsigned char fatal_error_trigger; - unsigned char reserved_1[116]; + /* + * Spoofs spare block percentage within the DIMM. + * Bit 0 - Enable/Disable Trigger + * 0x0h - Do Not/Disable Trigger + * 0x1h - Enable Trigger + * Bits 7:1 - Spare Block Percentage (valid values are between 0 and 100) + */ + unsigned char spare_block_percentage_trigger; + + /* + * Spoofs an unsafe shutdown on the next power cycle. + * 0x0h - Do Not/Disable Trigger + * 0x1h - Enable Trigger + */ + unsigned char unsafe_shutdown_trigger; + + unsigned char reserved_1[114]; } __attribute__((packed)); /* diff --git a/src/lib/nvm_management.h b/src/lib/nvm_management.h index 5063798..0288d49 100644 --- a/src/lib/nvm_management.h +++ b/src/lib/nvm_management.h @@ -317,6 +317,7 @@ enum error_type ERROR_TYPE_DIE_SPARING = 3, // Trigger or revert an artificial die sparing. ERROR_TYPE_SPARE_ALARM = 4, // Trigger or clear a spare capacity threshold alarm. ERROR_TYPE_MEDIA_FATAL_ERROR = 5, // Inject or clear a fake media fatal error. + ERROR_TYPE_DIRTY_SHUTDOWN = 6, // Inject or clear a dirty shutdown error. }; /*