smartmontools SVN Rev 5611
Utility to control and monitor storage systems with "S.M.A.R.T."
farmcmds.cpp
Go to the documentation of this file.
1/*
2 * farmcmds.cpp
3 *
4 * Home page of code is: https://www.smartmontools.org
5 *
6 * Copyright (C) 2021 - 2023 Seagate Technology LLC and/or its Affiliates
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11#include "config.h"
12
13#define __STDC_FORMAT_MACROS 1
14#include <inttypes.h>
15
16#include "farmcmds.h"
17
18#include "atacmds.h"
19#include "knowndrives.h"
20#include "scsicmds.h"
21#include "smartctl.h"
22
23/////////////////////////////////////////////////////////////////////////////////////////
24// Seagate ATA Field Access Reliability Metrics (FARM) log (Log 0xA6)
25
26/*
27 * Determines whether the current drive is an ATA Seagate drive
28 *
29 * @param drive: Pointer to drive struct containing ATA device information (*ata_identify_device)
30 * @param dbentry: Pointer to struct containing drive database entries (see drivedb.h) (drive_settings*)
31 * @return True if the drive is a Seagate drive, false otherwise (bool)
32 */
33bool ataIsSeagate(const ata_identify_device& drive, const drive_settings* dbentry) {
34 if (dbentry && str_starts_with(dbentry->modelfamily, "Seagate")) {
35 return true;
36 }
37 char model[40 + 1];
38 ata_format_id_string(model, drive.model, sizeof(model) - 1);
39 if (regular_expression("^ST[0-9]{3,5}[A-Z]{2}[A-Z0-9]{3,5}(-.*)?$").full_match(model)) {
40 return true;
41 }
42 return false;
43}
44
45
46/*
47 * Reads vendor-specific FARM log (GP Log 0xA6) data from Seagate
48 * drives and parses data into FARM log structures
49 * Returns parsed structure as defined in atacmds.h
50 *
51 * @param device: Pointer to instantiated device object (ata_device*)
52 * @param farmLog: Reference to parsed data in structure(s) with named members (ataFarmLog&)
53 * @param nsectors: Number of 512-byte sectors in this log (unsigned int)
54 * @return true if read successful, false otherwise (bool)
55 */
56bool ataReadFarmLog(ata_device* device, ataFarmLog& farmLog, unsigned nsectors) {
57 // Set up constants for FARM log
58 const size_t FARM_PAGE_SIZE = 16384;
59 const size_t FARM_ATTRIBUTE_SIZE = 8;
60 const size_t FARM_MAX_PAGES = 6;
61 const size_t FARM_SECTOR_SIZE = FARM_PAGE_SIZE * FARM_MAX_PAGES / nsectors;
62 const unsigned FARM_SECTORS_PER_PAGE = nsectors / FARM_MAX_PAGES;
63 const size_t FARM_CURRENT_PAGE_DATA_SIZE[FARM_MAX_PAGES] = {
64 sizeof(ataFarmHeader),
70 farmLog = { };
71 unsigned numSectorsToRead = (sizeof(ataFarmHeader) / FARM_SECTOR_SIZE) + 1;
72 // Go through each of the six pages of the FARM log
73 for (unsigned page = 0; page < FARM_MAX_PAGES; page++) {
74 // Reset the buffer
75 uint8_t pageBuffer[FARM_PAGE_SIZE] = { };
76 // Reset the current FARM log page
77 uint64_t currentFarmLogPage[FARM_PAGE_SIZE / FARM_ATTRIBUTE_SIZE] = { };
78 // Read the desired quantity of sectors from the current page into the buffer
79 bool readSuccessful = ataReadLogExt(device, 0xA6, 0, page * FARM_SECTORS_PER_PAGE, pageBuffer, numSectorsToRead);
80 if (!readSuccessful) {
81 jerr("Read FARM Log page %u failed\n\n", page);
82 return false;
83 }
84 // Read the page from the buffer, one attribute (8 bytes) at a time
85 for (unsigned pageOffset = 0; pageOffset < FARM_CURRENT_PAGE_DATA_SIZE[page]; pageOffset += FARM_ATTRIBUTE_SIZE) {
86 uint64_t currentMetric = 0;
87 // Read each attribute from the buffer, one byte at a time (combine 8 bytes, little endian)
88 for (unsigned byteOffset = 0; byteOffset < FARM_ATTRIBUTE_SIZE - 1; byteOffset++) {
89 currentMetric |= (uint64_t)(pageBuffer[pageOffset + byteOffset] |
90 (pageBuffer[pageOffset + byteOffset + 1] << 8)) << (byteOffset * 8);
91 }
92 // Check the status bit and strip it off if necessary
93 if (currentMetric >> 56 == 0xC0) {
94 currentMetric &= 0x00FFFFFFFFFFFFFFULL;
95 } else {
96 currentMetric = 0;
97 }
98 // Page 0 is the log header, so check the log signature to verify this is a FARM log
99 if (page == 0 && pageOffset == 0) {
100 if (currentMetric != 0x00004641524D4552) {
101 jerr("FARM log header is invalid (log signature=%" PRIu64 ")\n\n", currentMetric);
102 return false;
103 }
104 }
105 // Store value in structure for access to log by metric name
106 currentFarmLogPage[pageOffset / FARM_ATTRIBUTE_SIZE] = currentMetric;
107 }
108 // Copies array values to struct for named member access
109 // Check number of sectors to read for next page
110 switch (page) {
111 case 0:
112 memcpy(&farmLog.header, currentFarmLogPage, sizeof(farmLog.header));
113 numSectorsToRead = (sizeof(ataFarmDriveInformation) / FARM_SECTOR_SIZE) + 1;
114 break;
115 case 1:
116 memcpy(&farmLog.driveInformation, currentFarmLogPage, sizeof(farmLog.driveInformation));
117 numSectorsToRead = (sizeof(ataFarmWorkloadStatistics) / FARM_SECTOR_SIZE) + 1;
118 break;
119 case 2:
120 memcpy(&farmLog.workload, currentFarmLogPage, sizeof(farmLog.workload));
121 numSectorsToRead = (sizeof(ataFarmErrorStatistics) / FARM_SECTOR_SIZE) + 1;
122 break;
123 case 3:
124 memcpy(&farmLog.error, currentFarmLogPage, sizeof(farmLog.error));
125 numSectorsToRead = (sizeof(ataFarmEnvironmentStatistics) / FARM_SECTOR_SIZE) + 1;
126 break;
127 case 4:
128 memcpy(&farmLog.environment, currentFarmLogPage, sizeof(farmLog.environment));
129 numSectorsToRead = (sizeof(ataFarmReliabilityStatistics) / FARM_SECTOR_SIZE) + 1;
130 break;
131 case 5:
132 memcpy(&farmLog.reliability, currentFarmLogPage, sizeof(farmLog.reliability));
133 break;
134 }
135 }
136 return true;
137}
138
139/////////////////////////////////////////////////////////////////////////////////////////
140// Seagate SCSI Field Access Reliability Metrics (FARM) log (Log page 0x3D, sub-page 0x3)
141
142
143/*
144 * Determines whether the current drive is a SCSI Seagate drive
145 *
146 * @param scsi_vendor: Text of SCSI vendor field (char*)
147 * @return True if the drive is a Seagate drive, false otherwise (bool)
148 */
150 return (0 == memcmp(scsi_vendor, "SEAGATE", strlen("SEAGATE")));
151}
152
153/*
154 * Reads vendor-specific FARM log (SCSI log page 0x3D, sub-page 0x3) data from Seagate
155 * drives and parses data into FARM log structures
156 * Returns parsed structure as defined in scsicmds.h
157 *
158 * @param device: Pointer to instantiated device object (scsi_device*)
159 * @param farmLog: Reference to parsed data in structure(s) with named members (scsiFarmLog&)
160 * @return true if read successful, false otherwise (bool)
161 */
162bool scsiReadFarmLog(scsi_device* device, scsiFarmLog& farmLog) {
163 const uint32_t LOG_RESP_LONG_LEN = ((62 * 256) + 252);
164 const uint32_t GBUF_SIZE = 65532;
165 uint8_t gBuf[GBUF_SIZE];
166 const size_t FARM_ATTRIBUTE_SIZE = 8;
167 farmLog = { };
169 jerr("Read FARM Log page failed\n\n");
170 return false;
171 }
172 bool isParameterHeader = true;
173 // Log page header
174 farmLog.pageHeader.pageCode = gBuf[0];
175 farmLog.pageHeader.subpageCode = gBuf[1];
176 farmLog.pageHeader.pageLength = gBuf[2] << 8 | gBuf[3];
177 // Get rest of log
178 // Holds data for each SCSI parameter
179 uint64_t currentParameter[sizeof(gBuf) / FARM_ATTRIBUTE_SIZE] = { };
180 // Track index of current metric within each parameter
181 unsigned currentMetricIndex = 0;
182 // Track offset (in struct) of current SCSI parameter
183 unsigned currentParameterOffset = 0;
184 // Track offset in struct
185 scsiFarmParameterHeader currentParameterHeader;
186 for (unsigned pageOffset = sizeof(scsiFarmPageHeader);
187 pageOffset < (farmLog.pageHeader.pageLength + sizeof(scsiFarmPageHeader));
188 pageOffset += FARM_ATTRIBUTE_SIZE) {
189 // First get parameter header
190 if (isParameterHeader) {
191 // Clear old header
192 currentParameterHeader = scsiFarmParameterHeader();
193 // Set parameter values
194 currentParameterHeader.parameterCode = (gBuf[pageOffset] << 8) | gBuf[pageOffset + 1];
195 currentParameterHeader.parameterControl = gBuf[pageOffset + 2];
196 currentParameterHeader.parameterLength = gBuf[pageOffset + 3];
197 // Add offset to struct based on current parameter code
198 currentParameterOffset = sizeof(scsiFarmPageHeader);
199 if (currentParameterHeader.parameterCode >= 0x1) {
200 currentParameterOffset += sizeof(scsiFarmHeader);
201 }
202 if (currentParameterHeader.parameterCode >= 0x2) {
203 currentParameterOffset += sizeof(scsiFarmDriveInformation);
204 }
205 if (currentParameterHeader.parameterCode >= 0x3) {
206 currentParameterOffset += sizeof(scsiFarmWorkloadStatistics);
207 }
208 if (currentParameterHeader.parameterCode >= 0x4) {
209 currentParameterOffset += sizeof(scsiFarmErrorStatistics);
210 }
211 if (currentParameterHeader.parameterCode >= 0x5) {
212 currentParameterOffset += sizeof(scsiFarmEnvironmentStatistics);
213 }
214 if (currentParameterHeader.parameterCode >= 0x6) {
215 currentParameterOffset += sizeof(scsiFarmReliabilityStatistics);
216 }
217 if (currentParameterHeader.parameterCode >= 0x7) {
218 currentParameterOffset += sizeof(scsiFarmDriveInformation2);
219 }
220 if (currentParameterHeader.parameterCode >= 0x8) {
221 currentParameterOffset += sizeof(scsiFarmEnvironmentStatistics2);
222 }
223 // Skip "By Head" sections that are not present
224 if (currentParameterHeader.parameterCode >= 0x11 && currentParameterHeader.parameterCode <= 0x29) {
225 currentParameterOffset += sizeof(scsiFarmByHead) * (currentParameterHeader.parameterCode - 0x10);
226 } else if (currentParameterHeader.parameterCode >= 0x30 && currentParameterHeader.parameterCode <= 0x35) {
227 currentParameterOffset += sizeof(scsiFarmByHead) * ((0x2A - 0x10) + (currentParameterHeader.parameterCode - 0x30));
228 } else if (currentParameterHeader.parameterCode >= 0x40 && currentParameterHeader.parameterCode <= 0x4E) {
229 currentParameterOffset += sizeof(scsiFarmByHead) * ((0x2A - 0x10) + (0x36 - 0x30) + (currentParameterHeader.parameterCode - 0x40));
230 } else if (currentParameterHeader.parameterCode >= 0x50) {
231 currentParameterOffset += sizeof(scsiFarmByHead) * ((0x2A - 0x10) + (0x36 - 0x30) + (0x4F - 0x40));
232 }
233 if (currentParameterHeader.parameterCode >= 0x51) {
234 currentParameterOffset += sizeof(scsiFarmByActuator);
235 }
236 if (currentParameterHeader.parameterCode >= 0x52) {
237 currentParameterOffset += sizeof(scsiFarmByActuatorFLED);
238 }
239 if (currentParameterHeader.parameterCode >= 0x53) {
240 currentParameterOffset += sizeof(scsiFarmByActuatorReallocation);
241 }
242 if (currentParameterHeader.parameterCode >= 0x61) {
243 currentParameterOffset += sizeof(scsiFarmByActuator);
244 }
245 if (currentParameterHeader.parameterCode >= 0x62) {
246 currentParameterOffset += sizeof(scsiFarmByActuatorFLED);
247 }
248 if (currentParameterHeader.parameterCode >= 0x63) {
249 currentParameterOffset += sizeof(scsiFarmByActuatorReallocation);
250 }
251 if (currentParameterHeader.parameterCode >= 0x71) {
252 currentParameterOffset += sizeof(scsiFarmByActuator);
253 }
254 if (currentParameterHeader.parameterCode >= 0x72) {
255 currentParameterOffset += sizeof(scsiFarmByActuatorFLED);
256 }
257 if (currentParameterHeader.parameterCode >= 0x73) {
258 currentParameterOffset += sizeof(scsiFarmByActuatorReallocation);
259 }
260 if (currentParameterHeader.parameterCode >= 0x81) {
261 currentParameterOffset += sizeof(scsiFarmByActuator);
262 }
263 if (currentParameterHeader.parameterCode >= 0x82) {
264 currentParameterOffset += sizeof(scsiFarmByActuatorFLED);
265 }
266 if (currentParameterHeader.parameterCode >= 0x83) {
267 currentParameterOffset += sizeof(scsiFarmByActuatorReallocation);
268 }
269 // Copy parameter header to struct
270 memcpy(reinterpret_cast<char*>(&farmLog) + currentParameterOffset,
271 &currentParameterHeader,
273 // Fix offset
274 pageOffset += sizeof(scsiFarmParameterHeader);
275 // No longer setting header
276 isParameterHeader = false;
277 currentMetricIndex = 0;
278 }
279 uint64_t currentMetric = 0;
280 // Read each attribute from the buffer, one byte at a time (combine 8 bytes, big endian)
281 for (unsigned byteOffset = 0; byteOffset < FARM_ATTRIBUTE_SIZE; byteOffset++) {
282 currentMetric |= (uint64_t)(gBuf[pageOffset + byteOffset]) << ((7 - byteOffset) * 8);
283 }
284 // Check the status bit and strip it off if necessary
285 if (currentMetric >> 56 == 0xC0) {
286 currentMetric &= 0x00FFFFFFFFFFFFFFULL;
287 // Parameter 0 is the log header, so check the log signature to verify this is a FARM log
288 if (pageOffset == sizeof(scsiFarmPageHeader)) {
289 if (currentMetric != 0x00004641524D4552) {
290 jerr("FARM log header is invalid (log signature=%" PRIu64 ")\n\n", currentMetric);
291 return false;
292 }
293 }
294 // If a parameter header is reached, set the values
295 } else if (currentParameterHeader.parameterLength <= currentMetricIndex * FARM_ATTRIBUTE_SIZE) {
296 // Apply header for NEXT parameter
297 isParameterHeader = true;
298 pageOffset -= FARM_ATTRIBUTE_SIZE;
299 // Copy data for CURRENT parameter to struct (skip parameter header which has already been assigned)
300 memcpy(reinterpret_cast<char*>(&farmLog) + currentParameterOffset + sizeof(scsiFarmParameterHeader),
301 currentParameter,
302 currentParameterHeader.parameterLength);
303 continue;
304 } else {
305 currentMetric = 0;
306 }
307 currentParameter[currentMetricIndex] = currentMetric;
308 currentMetricIndex++;
309 }
310 return true;
311}
bool ataReadLogExt(ata_device *device, unsigned char logaddr, unsigned char features, unsigned page, void *data, unsigned nsectors)
Definition: atacmds.cpp:1111
void ata_format_id_string(char *out, const unsigned char *in, int n)
Definition: atacmds.cpp:762
ATA device access.
Wrapper class for POSIX regex(3) or std::regex Supports copy & assignment and is compatible with STL ...
Definition: utility.h:222
SCSI device access.
bool scsiReadFarmLog(scsi_device *device, scsiFarmLog &farmLog)
Definition: farmcmds.cpp:162
bool scsiIsSeagate(char *scsi_vendor)
Definition: farmcmds.cpp:149
bool ataIsSeagate(const ata_identify_device &drive, const drive_settings *dbentry)
Definition: farmcmds.cpp:33
bool ataReadFarmLog(ata_device *device, ataFarmLog &farmLog, unsigned nsectors)
Definition: farmcmds.cpp:56
int scsiLogSense(scsi_device *device, int pagenum, int subpagenum, uint8_t *pBuf, int bufLen, int known_resp_len)
Definition: scsicmds.cpp:873
#define SEAGATE_FARM_LPAGE
Definition: scsicmds.h:258
#define SEAGATE_FARM_CURRENT_L_SPAGE
Definition: scsicmds.h:262
static char scsi_vendor[8+1]
Definition: scsiprint.cpp:94
#define LOG_RESP_LONG_LEN
Definition: scsiprint.cpp:43
uint8_t gBuf[GBUF_SIZE]
Definition: scsiprint.cpp:41
#define GBUF_SIZE
Definition: scsiprint.cpp:34
void void void void jerr(const char *fmt,...) __attribute_format_printf(1
ataFarmErrorStatistics error
Definition: farmcmds.h:266
ataFarmHeader header
Definition: farmcmds.h:263
ataFarmReliabilityStatistics reliability
Definition: farmcmds.h:268
ataFarmWorkloadStatistics workload
Definition: farmcmds.h:265
ataFarmEnvironmentStatistics environment
Definition: farmcmds.h:267
ataFarmDriveInformation driveInformation
Definition: farmcmds.h:264
unsigned char model[40]
Definition: atacmds.h:120
const char * modelfamily
Definition: knowndrives.h:19
scsiFarmPageHeader pageHeader
Definition: farmcmds.h:604
uint8_t subpageCode
Definition: farmcmds.h:279
uint16_t pageLength
Definition: farmcmds.h:280
uint8_t pageCode
Definition: farmcmds.h:278
bool str_starts_with(const char *str, const char *prefix)
Definition: utility.h:52