From e98dbea5db439aff7f42254cf485889d54306f89 Mon Sep 17 00:00:00 2001
From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Fri, 6 Feb 2009 22:33:05 +0000
Subject: [PATCH] Add smartctl option '-l xerror[,NUM]' to print ATA SMART
 Extended Comprehensive Error Log.

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@2731 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/CHANGELOG     |   5 +-
 sm5/atacmds.cpp   |  38 +++++-
 sm5/atacmds.h     |  91 +++++++++++++-
 sm5/ataprint.cpp  | 314 +++++++++++++++++++++++++++++++++++++---------
 sm5/ataprint.h    |   6 +-
 sm5/smartctl.8.in |  26 ++--
 sm5/smartctl.cpp  |  20 ++-
 7 files changed, 420 insertions(+), 80 deletions(-)

diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 81bead38e..5861ebbed 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.768 2009/02/01 23:02:54 manfred99 Exp $
+$Id: CHANGELOG,v 1.769 2009/02/06 22:33:05 chrfranke Exp $
 
 The most recent version of this file is:
 http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
@@ -41,6 +41,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] Add smartctl option '-l xerror[,NUM]' to print
+       ATA SMART Extended Comprehensive Error Log (GP Log 0x03).
+
   [MS] knowndrives.cpp update:
        Added remaining WD Scorpio Blue SATA II drives
 
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index accfa9f5f..28cad395a 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -39,7 +39,7 @@
 
 #include <algorithm> // std::sort
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.210 2009/01/08 22:05:38 dpgilbert Exp $"
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.211 2009/02/06 22:33:05 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -1062,7 +1062,8 @@ bool ataReadLogExt(ata_device * device, unsigned char logaddr,
   in.in_regs.lba_mid_16   = page;
 
   if (!device->ata_pass_through(in)) { // TODO: Debug output
-    pout("ATA_READ_LOG_EXT failed: %s\n", device->get_errmsg());
+    pout("ATA_READ_LOG_EXT (addr=0x%02x:0x%02x, page=%u, n=%u) failed: %s\n",
+         logaddr, features, page, nsectors, device->get_errmsg());
     return false;
   }
   return true;
@@ -1380,6 +1381,39 @@ int ataReadErrorLog (ata_device * device, struct ata_smart_errorlog *data){
   return 0;
 }
 
+// Read Extended Comprehensive Error Log
+bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
+                        unsigned nsectors)
+{
+  if (!ataReadLogExt(device, 0x03, 0x00, 0, log, nsectors)) {
+    // Retry with single sectors, some disks (Samsung HD301LJ),
+    // don't support multi sector reads of this log.
+    for (unsigned i = 0; i < nsectors; i++) {
+      if (!ataReadLogExt(device, 0x03, 0x00, i, log+i, 1))
+        return false;
+    }
+  }
+
+  for (unsigned i = 0; i < nsectors; i++) {
+    if (checksum((const unsigned char *)(log + 1)))
+      checksumwarning("SMART ATA Extended Comprehensive Error Log Structure");
+  }
+
+  if (isbigendian()) {
+    swapx(&log->device_error_count);
+    swapx(&log->error_log_index);
+
+    for (unsigned i = 0; i < nsectors; i++) {
+      for (unsigned j = 0; j < 4; j++)
+        swapx(&log->error_logs[i].commands[j].timestamp);
+      swapx(&log->error_logs[i].error.timestamp);
+    }
+  }
+
+  return true;
+}
+
+
 int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt *data){
   
   // get data from device
diff --git a/sm5/atacmds.h b/sm5/atacmds.h
index b96660896..d9244f5c7 100644
--- a/sm5/atacmds.h
+++ b/sm5/atacmds.h
@@ -25,7 +25,7 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.102 2008/10/11 14:18:07 chrfranke Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.103 2009/02/06 22:33:05 chrfranke Exp $\n"
 
 #include "dev_interface.h" // ata_device
 
@@ -302,6 +302,90 @@ struct ata_smart_errorlog {
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_smart_errorlog, 512);
 
+
+// Extendend Comprehensive Error Log data structures
+// See Section A.7 of
+//   AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
+//   T13/1699-D Revision 6a (Working Draft), September 6, 2008.
+
+// Command data structure
+// Table A.9 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_command
+{
+  unsigned char device_control_register;
+  unsigned char features_register;
+  unsigned char features_register_hi;
+  unsigned char count_register;
+  unsigned char count_register_hi;
+  unsigned char lba_low_register;
+  unsigned char lba_low_register_hi;
+  unsigned char lba_mid_register;
+  unsigned char lba_mid_register_hi;
+  unsigned char lba_high_register;
+  unsigned char lba_high_register_hi;
+  unsigned char device_register;
+  unsigned char command_register;
+
+  unsigned char reserved;
+  unsigned int timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_command, 18);
+
+// Error data structure
+// Table A.10 T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_error
+{
+  unsigned char device_control_register;
+  unsigned char error_register;
+  unsigned char count_register;
+  unsigned char count_register_hi;
+  unsigned char lba_low_register;
+  unsigned char lba_low_register_hi;
+  unsigned char lba_mid_register;
+  unsigned char lba_mid_register_hi;
+  unsigned char lba_high_register;
+  unsigned char lba_high_register_hi;
+  unsigned char device_register;
+  unsigned char status_register;
+
+  unsigned char extended_error[19];
+  unsigned char state;
+  unsigned short timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error, 34);
+
+// Error log data structure
+// Table A.8 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_error_log
+{
+  ata_smart_exterrlog_command commands[5];
+  ata_smart_exterrlog_error error;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error_log, 124);
+
+// Ext. Comprehensive SMART error log
+// Table A.7 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog
+{
+  unsigned char version;
+  unsigned char reserved1;
+  unsigned short error_log_index;
+  ata_smart_exterrlog_error_log error_logs[4];
+  unsigned short device_error_count;
+  unsigned char reserved2[9];
+  unsigned char checksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog, 512);
+
+
 // Table 45 of T13/1321D Rev 1 spec (Self-test log descriptor entry)
 #pragma pack(1)
 struct ata_smart_selftestlog_struct {
@@ -376,8 +460,6 @@ ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512);
 #define SELECTIVE_FLAG_PENDING (0x0008)
 #define SELECTIVE_FLAG_ACTIVE  (0x0010)
 
-class ata_device;
-
 
 // SCT (SMART Command Transport) data structures
 // See Sections 8.2 and 8.3 of:
@@ -485,6 +567,9 @@ bool ataReadLogExt(ata_device * device, unsigned char logaddr,
 // Read SMART Log page(s)
 bool ataReadSmartLog(ata_device * device, unsigned char logaddr,
                      void * data, unsigned nsectors);
+// Read SMART Extended Comprehensive Error Log
+bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
+                        unsigned nsectors);
 
 // Read SCT information
 int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts);
diff --git a/sm5/ataprint.cpp b/sm5/ataprint.cpp
index 88137c4a1..01df05d16 100644
--- a/sm5/ataprint.cpp
+++ b/sm5/ataprint.cpp
@@ -43,7 +43,7 @@
 #include "utility.h"
 #include "knowndrives.h"
 
-const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.205 2009/01/29 20:29:00 chrfranke Exp $"
+const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.206 2009/02/06 22:33:05 chrfranke Exp $"
 ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // for passing global control variables
@@ -56,9 +56,8 @@ static const char * infofound(const char *output) {
 
 /* For the given Command Register (CR) and Features Register (FR), attempts
  * to construct a string that describes the contents of the Status
- * Register (ST) and Error Register (ER).  The string is dynamically allocated
- * memory and the return value is a pointer to this string.  It is up to the
- * caller to free this memory.  If there is insufficient memory or if the
+ * Register (ST) and Error Register (ER).  The caller passes the string
+ * buffer and the return value is a pointer to this string.  If the
  * meanings of the flags of the error register are not known for the given
  * command then it returns NULL.
  *
@@ -68,13 +67,16 @@ static const char * infofound(const char *output) {
  * to produce errors).  If many more are to be added then this function
  * should probably be redesigned.
  */
-static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
+
+static const char * construct_st_er_desc(
+  char * s,
+  unsigned char CR, unsigned char FR,
+  unsigned char ST, unsigned char ER,
+  unsigned short SC,
+  const ata_smart_errorlog_error_struct * lba28_regs,
+  const ata_smart_exterrlog_error * lba48_regs
+)
 {
-  unsigned char CR=data->commands[4].commandreg;
-  unsigned char FR=data->commands[4].featuresreg;
-  unsigned char ST=data->error_struct.status;
-  unsigned char ER=data->error_struct.error_register;
-  char *s;
   const char *error_flag[8];
   int i, print_lba=0, print_sector=0;
 
@@ -143,7 +145,7 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
     error_flag[1] = nm;
     error_flag[0] = ccto;
     print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
+    print_sector=SC;
     break;
   case 0x3A:  // WRITE STREAM DMA
   case 0x3B:  // WRITE STREAM PIO
@@ -157,7 +159,7 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
     error_flag[1] = nm;
     error_flag[0] = ccto;
     print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
+    print_sector=SC;
     break;
   case 0x25:  /* READ DMA EXT */
   case 0x26:  // READ DMA QUEUED EXT
@@ -174,7 +176,7 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
     error_flag[0] = amnf;
     print_lba=1;
     if (CR==0x25 || CR==0xC8)
-      print_sector=(int)data->error_struct.sector_count;
+      print_sector=SC;
     break;
   case 0x30:  /* WRITE SECTOR(S) */
   case 0x31:  // WRITE SECTOR(S)
@@ -305,7 +307,7 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
     error_flag[0] = amnf;
     print_lba=1;
     if (CR==0x35)
-      print_sector=(int)data->error_struct.sector_count;
+      print_sector=SC;
     break;
   case 0xE4: // READ BUFFER
   case 0xE8: // WRITE BUFFER
@@ -315,10 +317,6 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
     return NULL;
   }
 
-  /* 256 bytes -- that'll be plenty (OK, this is lazy!) */
-  if (!(s = (char *)malloc(256))) // TODO: new/delete or std::string
-    return s;
-
   s[0] = '\0';
 
   /* We ignore any status flags other than Device Fault and Error */
@@ -344,34 +342,77 @@ static char *construct_st_er_desc(const ata_smart_errorlog_struct *data)
   // Address (LBA) at which the read or write failed.
   if (print_lba) {
     char tmp[128];
-    int lba;
-
-    // bits 24-27: bits 0-3 of DH
-    lba   = 0xf & data->error_struct.drive_head;
-    lba <<= 8;
-    // bits 16-23: CH
-    lba  |= data->error_struct.cylinder_high;
-    lba <<= 8;
-    // bits 8-15:  CL
-    lba  |= data->error_struct.cylinder_low;
-    lba <<= 8;
-    // bits 0-7:   SN
-    lba  |= data->error_struct.sector_number;
-
     // print number of sectors, if known, and append to print string
     if (print_sector) {
       snprintf(tmp, 128, " %d sectors", print_sector);
       strcat(s, tmp);
     }
 
-    // print LBA, and append to print string
-    snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba);
-    strcat(s, tmp);
+    if (lba28_regs) {
+      unsigned lba;
+      // bits 24-27: bits 0-3 of DH
+      lba   = 0xf & lba28_regs->drive_head;
+      lba <<= 8;
+      // bits 16-23: CH
+      lba  |= lba28_regs->cylinder_high;
+      lba <<= 8;
+      // bits 8-15:  CL
+      lba  |= lba28_regs->cylinder_low;
+      lba <<= 8;
+      // bits 0-7:   SN
+      lba  |= lba28_regs->sector_number;
+      snprintf(tmp, 128, " at LBA = 0x%08x = %u", lba, lba);
+      strcat(s, tmp);
+    }
+    else if (lba48_regs) {
+      // This assumes that upper LBA registers are 0 for 28-bit commands
+      // (TODO: detect 48-bit commands above)
+      uint64_t lba48;
+      lba48   = lba48_regs->lba_high_register_hi;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_mid_register_hi;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_low_register_hi;
+      lba48  |= lba48_regs->device_register & 0xf;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_high_register;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_mid_register;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_low_register;
+      snprintf(tmp, 128, " at LBA = 0x%08"PRIx64" = %"PRIu64, lba48, lba48);
+      strcat(s, tmp);
+    }
   }
 
   return s;
 }
 
+static inline const char * construct_st_er_desc(char * s,
+  const ata_smart_errorlog_struct * data)
+{
+  return construct_st_er_desc(s,
+    data->commands[4].commandreg,
+    data->commands[4].featuresreg,
+    data->error_struct.status,
+    data->error_struct.error_register,
+    data->error_struct.sector_count,
+    &data->error_struct, (const ata_smart_exterrlog_error *)0);
+}
+
+static inline const char * construct_st_er_desc(char * s,
+  const ata_smart_exterrlog_error_log * data)
+{
+  return construct_st_er_desc(s,
+    data->commands[4].command_register,
+    data->commands[4].features_register,
+    data->error.status_register,
+    data->error.error_register,
+    data->error.count_register_hi << 8 | data->error.count_register,
+    (const ata_smart_errorlog_error_struct *)0, &data->error);
+}
+
+
 // This returns the capacity of a disk drive and also prints this into
 // a string, using comma separators to make it easier to read.  If the
 // drive doesn't support LBA addressing or has no user writable
@@ -1027,6 +1068,22 @@ static void PrintSataPhyEventCounters(const unsigned char * data, bool reset)
   pout("\n");
 }
 
+// Get description for 'state' value from SMART Error Logs
+static const char * get_error_log_state_desc(unsigned state)
+{
+  state &= 0x0f;
+  switch (state){
+    case 0x0: return "in an unknown state";
+    case 0x1: return "sleeping";
+    case 0x2: return "in standby mode";
+    case 0x3: return "active or idle";
+    case 0x4: return "doing SMART Offline or Self-test";
+  default:
+    return (state < 0xb ? "in a reserved state"
+                        : "in a vendor specific state");
+  }
+}
+
 // returns number of errors
 static int ataPrintSmartErrorlog(const ata_smart_errorlog *data)
 {
@@ -1075,7 +1132,6 @@ static int ataPrintSmartErrorlog(const ata_smart_errorlog *data)
   
   // now step through the five error log data structures (table 39 of spec)
   for (int k = 4; k >= 0; k-- ) {
-    char *st_er_desc;
 
     // The error log data structure entries are a circular buffer
     int j, i=(data->error_log_pointer+k)%5;
@@ -1085,23 +1141,9 @@ static int ataPrintSmartErrorlog(const ata_smart_errorlog *data)
     // Spec says: unused error log structures shall be zero filled
     if (nonempty((unsigned char*)elog,sizeof(*elog))){
       // Table 57 of T13/1532D Volume 1 Revision 3
-      const char *msgstate;
-      int bits=summary->state & 0x0f;
+      const char *msgstate = get_error_log_state_desc(summary->state);
       int days = (int)summary->timestamp/24;
 
-      switch (bits){
-      case 0x00: msgstate="in an unknown state";break;
-      case 0x01: msgstate="sleeping"; break;
-      case 0x02: msgstate="in standby mode"; break;
-      case 0x03: msgstate="active or idle"; break;
-      case 0x04: msgstate="doing SMART Offline or Self-test"; break;
-      default:   
-        if (bits<0x0b)
-          msgstate="in a reserved state";
-        else
-          msgstate="in a vendor specific state";
-      }
-
       // See table 42 of ATA5 spec
       PRINT_ON(con);
       pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
@@ -1121,11 +1163,10 @@ static int ataPrintSmartErrorlog(const ata_smart_errorlog *data)
            (int)summary->drive_head);
       // Add a description of the contents of the status and error registers
       // if possible
-      st_er_desc = construct_st_er_desc(elog);
-      if (st_er_desc) {
+      char descbuf[256];
+      const char * st_er_desc = construct_st_er_desc(descbuf, elog);
+      if (st_er_desc)
         pout("  %s", st_er_desc);
-        free(st_er_desc);
-      }
       pout("\n\n");
       pout("  Commands leading to the command that caused the error were:\n"
            "  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name\n"
@@ -1163,6 +1204,143 @@ static int ataPrintSmartErrorlog(const ata_smart_errorlog *data)
   return data->ata_error_count;  
 }
 
+// Print SMART Extended Comprehensive Error Log (GP Log 0x03)
+static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log,
+                                 unsigned nsectors, unsigned max_errors)
+{
+  pout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
+       log->version, nsectors);
+
+  if (!log->device_error_count) {
+    pout("No Errors Logged\n\n");
+    return 0;
+  }
+  PRINT_ON(con);
+
+  // If log pointer out of range, return
+  if (log->error_log_index >= nsectors * 4){
+    pout("Invalid Error Log index = 0x%04x\n", log->error_log_index);
+    return 0;
+  }
+
+  // Calculate #errors to print
+  unsigned errcnt = log->device_error_count;
+
+  if (errcnt <= nsectors * 4)
+    pout("Device Error Count: %u\n", log->device_error_count);
+  else {
+    errcnt = nsectors * 4;
+    pout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
+         log->device_error_count, errcnt);
+  }
+
+  if (max_errors < errcnt)
+    errcnt = max_errors;
+
+  PRINT_OFF(con);
+  pout("\tCR     = Command Register\n"
+       "\tFEATR  = Features Register\n"
+       "\tCOUNT  = Count (was: Sector Count) Register\n"
+       "\tLBA_48 = Upper bytes of LBA High/Mid/Low Registers ]  ATA-8\n"
+       "\tLH     = LBA High (was: Cylinder High) Register    ]   LBA\n"
+       "\tLM     = LBA Mid (was: Cylinder Low) Register      ] Register\n"
+       "\tLL     = LBA Low (was: Sector Number) Register     ]\n"
+       "\tDV     = Device (was: Device/Head) Register\n"
+       "\tDC     = Device Control Register\n"
+       "\tER     = Error register\n"
+       "\tST     = Status register\n"
+       "Powered_Up_Time is measured from power on, and printed as\n"
+       "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n"
+       "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
+
+  // Iterate through circular buffer in reverse direction
+  for (unsigned i = 0, errnum = log->device_error_count, erridx = log->error_log_index;
+       i < errcnt; i++, errnum--, erridx = (erridx > 0 ? erridx - 1 : nsectors * 4 - 1)) {
+
+    const ata_smart_exterrlog_error_log & entry = log[erridx / 4].error_logs[erridx % 4];
+
+    // Skip unused entries
+    if (!nonempty(&entry, sizeof(entry)))
+      continue;
+
+    // Print error information
+    PRINT_ON(con);
+    const ata_smart_exterrlog_error & err = entry.error;
+    pout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
+         errnum, erridx, err.timestamp, err.timestamp / 24, err.timestamp % 24);
+    PRINT_OFF(con);
+
+    pout("  When the command that caused the error occurred, the device was %s.\n\n",
+      get_error_log_state_desc(err.state));
+
+    // Print registers
+    pout("  After command completion occurred, registers were:\n"
+         "  ER ST COUNT  LBA_48  LH LM LL DV DC\n"
+         "  -- -- == -- == == == -- -- -- -- --\n"
+         "  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+         err.error_register,
+         err.status_register,
+         err.count_register_hi,
+         err.count_register,
+         err.lba_high_register_hi,
+         err.lba_mid_register_hi,
+         err.lba_low_register_hi,
+         err.lba_high_register,
+         err.lba_mid_register,
+         err.lba_low_register,
+         err.device_register,
+         err.device_control_register);
+
+    // Add a description of the contents of the status and error registers
+    // if possible
+    char descbuf[256];
+    const char * st_er_desc = construct_st_er_desc(descbuf, &entry);
+    if (st_er_desc)
+      pout("  %s", st_er_desc);
+    pout("\n\n");
+
+    // Print command history
+    pout("  Commands leading to the command that caused the error were:\n"
+         "  CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name\n"
+         "  -- == -- == -- == == == -- -- -- -- --  ---------------  --------------------\n");
+    for (int ci = 4; ci >= 0; ci--) {
+      const ata_smart_exterrlog_command & cmd = entry.commands[ci];
+
+      // Skip unused entries
+      if (!nonempty(&cmd, sizeof(cmd)))
+        continue;
+
+      // Print registers, timestamp and ATA command name
+      char timestring[32];
+      MsecToText(cmd.timestamp, timestring);
+
+      pout("  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s  %s\n",
+           cmd.command_register,
+           cmd.features_register_hi,
+           cmd.features_register,
+           cmd.count_register_hi,
+           cmd.count_register,
+           cmd.lba_high_register_hi,
+           cmd.lba_mid_register_hi,
+           cmd.lba_low_register_hi,
+           cmd.lba_high_register,
+           cmd.lba_mid_register,
+           cmd.lba_low_register,
+           cmd.device_register,
+           cmd.device_control_register,
+           timestring,
+           look_up_ata_command(cmd.command_register, cmd.features_register));
+    }
+    pout("\n");
+  }
+
+  PRINT_ON(con);
+  if (con->printing_switchable)
+    pout("\n");
+  PRINT_OFF(con);
+  return log->device_error_count;
+}
+
 static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log, const ata_smart_values * sv)
 {
   int i,field1,field2;
@@ -1683,7 +1861,8 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // START OF READ-ONLY OPTIONS APART FROM -V and -i
   if (   con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog
-      || con->smartselftestlog || con->selectivetestlog || con->scttempsts || con->scttemphist     )
+      || con->smartselftestlog || con->selectivetestlog || con->scttempsts || con->scttemphist
+      || options.smart_ext_error_log                                                               )
     pout("=== START OF READ SMART DATA SECTION ===\n");
   
   // Check SMART status (use previously returned value)
@@ -1782,8 +1961,8 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // Print SMART and/or GP log Directory and/or logs
   if (   options.gp_logdir || options.smart_logdir
-      || options.sataphy
-      || !options.log_requests.empty()            ) {
+      || options.sataphy || options.smart_ext_error_log
+      || !options.log_requests.empty()                 ) {
 
     bool gpl_cap = !!isGeneralPurposeLoggingCapable(&drive);
     bool gpl_try = gpl_cap;
@@ -1804,7 +1983,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
       // Detect directories needed
       bool need_smart_logdir = options.smart_logdir;
-      bool need_gp_logdir    = options.gp_logdir;
+      bool need_gp_logdir    = options.gp_logdir || options.smart_ext_error_log;
       unsigned i;
       for (i = 0; i < options.log_requests.size(); i++) {
         if (options.log_requests[i].gpl)
@@ -1895,6 +2074,23 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
           PrintLogPages(type, log_buf.data() + offs*512, req.logaddr, req.page, ns, max_nsectors);
       }
 
+      // Print SMART Extendend Comprehensive Error Log
+      if (options.smart_ext_error_log) {
+        unsigned nsectors = GetNumLogSectors(gplogdir, 0x03, true);
+        if (!nsectors)
+          pout("SMART Extended Comprehensive Error Log (GP Log 0x03) does not exist\n");
+        else if (nsectors >= 256)
+          pout("SMART Extended Comprehensive Error Log size %u not supported\n", nsectors);
+        else {
+          raw_buffer log_03_buf(nsectors * 512);
+          ata_smart_exterrlog * log_03 = (ata_smart_exterrlog *)log_03_buf.data();
+          if (!ataReadExtErrorLog(device, log_03, nsectors))
+            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+          else
+            PrintSmartExtErrorLog(log_03, nsectors, options.smart_ext_error_log);
+        }
+      }
+
       // Print SATA Phy Event Counters
       if (options.sataphy) {
         unsigned char log_11[512] = {0, };
diff --git a/sm5/ataprint.h b/sm5/ataprint.h
index 912947726..c9e8676f0 100644
--- a/sm5/ataprint.h
+++ b/sm5/ataprint.h
@@ -25,7 +25,7 @@
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.37 2008/09/06 20:08:35 chrfranke Exp $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.38 2009/02/06 22:33:05 chrfranke Exp $\n"
 
 #include <vector>
 
@@ -48,12 +48,14 @@ struct ata_print_options
 {
   bool sataphy, sataphy_reset;
   bool gp_logdir, smart_logdir;
+  unsigned smart_ext_error_log;
 
   std::vector<ata_log_request> log_requests;
 
   ata_print_options()
     : sataphy(false), sataphy_reset(false),
-      gp_logdir(false), smart_logdir(false)
+      gp_logdir(false), smart_logdir(false),
+      smart_ext_error_log(0)
     { }
 };
 
diff --git a/sm5/smartctl.8.in b/sm5/smartctl.8.in
index f9d87e76f..c29fce3af 100644
--- a/sm5/smartctl.8.in
+++ b/sm5/smartctl.8.in
@@ -1,7 +1,7 @@
 .ig
  Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
- $Id: smartctl.8.in,v 1.118 2009/01/14 02:39:00 sxzzsf Exp $
+ $Id: smartctl.8.in,v 1.119 2009/02/06 22:33:05 chrfranke Exp $
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
@@ -736,8 +736,8 @@ Selective Self\-Test Log [ATA only], the Log Directory [ATA only], or
 the Background Scan Results Log [SCSI only].
 The valid arguments to this option are:
 
-.I error
-\- prints only the SMART error log.  SMART disks maintain a log of the
+.I error [ATA]
+\- prints the Summary SMART error log.  SMART disks maintain a log of the
 most recent five non\-trivial errors. For each of these errors, the
 disk power\-on lifetime at which the error occurred is recorded, as is
 the device status (idle, standby, etc) at the time of the error.  For
@@ -815,6 +815,19 @@ receives a command which is not implemented or is not valid.
 \- prints the error counter log pages for reads, write and verifies.
 The verify row is only output if it has an element other than zero.
 
+.I xerror[,NUM] [ATA]
+\- [NEW EXPERIMENTAL SMARTCTL FEATURE] prints the Extended
+Comprehensive SMART error log (General Purpose Log address 0x03).
+Unlike the Summary SMART error log (see \'\-l error\' above),
+it provides sufficient space to log the contents of the 48-bit
+LBA register set introduced with ATA-6.  It also supports logs
+with more than one sector.  Each sector holds up to 4 log entries.
+The actual number of log sectors is vendor specific, typical values
+are 2 (Samsung) or 5 (Seagate).
+
+If the optional parameter NUM is specified, only the NUM most
+recent log entries are printed.  Otherwise, all entries are printed.
+
 .I selftest
 \- prints the SMART self\-test log.  The disk maintains a self\-test log
 showing the results of the self tests, which can be run using the
@@ -882,11 +895,6 @@ If your version of smartctl supports 48-bit ATA commands, both the
 General Purpose Log (GPL) and SMART Log (SL) directories are printed in
 one combined table. The output can be restricted to the GPL directory or
 SL directory by \'\-l directory,q\' or \'\-l directory,s\' respectively.
-[Please note: this is a new, experimental
-feature.  We would like to add support for printing the contents of
-extended and comprehensive SMART self\-test and error logs.  If your
-disk supports these, and you would like to assist, please contact the
-\fBsmartmontools\fP developers.]
 
 .I background [SCSI]
 \- the background scan results log outputs information derived from
@@ -1644,7 +1652,7 @@ these documents may be found in the References section of the
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartctl.8.in,v 1.118 2009/01/14 02:39:00 sxzzsf Exp $
+$Id: smartctl.8.in,v 1.119 2009/02/06 22:33:05 chrfranke Exp $
 .\" Local Variables:	         
 .\" mode: nroff         
 .\" End:
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index cbad5dab5..e8337a259 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -64,7 +64,7 @@ extern const char *os_solaris_ata_s_cvsid;
 extern const char *cciss_c_cvsid;
 #endif
 extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.192 2008/10/24 19:51:54 chrfranke Exp $"
+const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.193 2009/02/06 22:33:05 chrfranke Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // This is a block containing all the "control variables".  We declare
@@ -197,7 +197,8 @@ void Usage (void){
 "  -l TYPE, --log=TYPE\n"
 "        Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
 "                               background, sataphy[,reset], scttemp[sts,hist]\n"
-"                               gplog,N[,RANGE], smartlog,N[,RANGE]\n\n"
+"                               gplog,N[,RANGE], smartlog,N[,RANGE],\n"
+"                               xerror[,N]\n\n"
 "  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
 "        Set display OPTION for vendor Attribute N (see man page)\n\n"
 "  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
@@ -219,7 +220,8 @@ void Usage (void){
 "  -A        Show device SMART vendor-specific Attributes and values    (ATA)\n"
 "  -l TYPE   Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
 "                                   background, sataphy[,reset], scttemp[sts,hist]\n"
-"                                   gplog,N[,RANGE], smartlog,N[,RANGE]\n"
+"                                   gplog,N[,RANGE], smartlog,N[,RANGE],\n"
+"                                   xerror[,N]\n"
 "  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
 "  -F TYPE   Use firmware bug workaround: none, samsung, samsung2,      (ATA)\n"
 "                                         samsung3, swapid\n"
@@ -271,7 +273,7 @@ const char *getvalidarglist(char opt) {
     return "on, off";
   case 'l':
     return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], "
-           "sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE]";
+           "sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], xerror[,N]";
   case 'P':
     return "use, ignore, show, showall";
   case 't':
@@ -524,6 +526,16 @@ const char * ParseOpts (int argc, char** argv, ata_print_options & options)
         con->scttempsts = TRUE;
       } else if (!strcmp(optarg,"scttemphist")) {
         con->scttemphist = TRUE;
+
+      } else if (!strncmp(optarg,"xerror", sizeof("xerror")-1)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = ~0U;
+        sscanf(optarg, "xerror%n,%u%n", &n1, &val, &n2);
+        if (!((n1 == len || n2 == len) && val > 0))
+          badarg = TRUE;
+        else
+          options.smart_ext_error_log = val;
+
       } else if (   !strncmp(optarg, "gplog,"   , sizeof("gplog,"   )-1)
                  || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) {
         unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0;
-- 
GitLab