]>
Commit | Line | Data |
---|---|---|
4d59bff9 GG |
1 | /* |
2 | * scsiata.cpp | |
3 | * | |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
6 | * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * (for example COPYING); if not, write to the Free | |
15 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
16 | * | |
17 | * The code in this file is based on the SCSI to ATA Translation (SAT) | |
18 | * draft found at http://www.t10.org . The original draft used for this | |
19 | * code is sat-r08.pdf which is not too far away from becoming a | |
20 | * standard. The SAT commands of interest to smartmontools are the | |
21 | * ATA PASS THROUGH SCSI (16) and ATA PASS THROUGH SCSI (12) defined in | |
22 | * section 12 of that document. | |
23 | * | |
24 | * With more transports "hiding" SATA disks (and other S-ATAPI devices) | |
25 | * behind a SCSI command set, accessing special features like SMART | |
26 | * information becomes a challenge. The SAT standard offers ATA PASS | |
27 | * THROUGH commands for special usages. Note that the SAT layer may | |
28 | * be inside a generic OS layer (e.g. libata in linux), in a host | |
29 | * adapter (HA or HBA) firmware, or somewhere on the interconnect | |
30 | * between the host computer and the SATA devices (e.g. a RAID made | |
31 | * of SATA disks and the RAID talks "SCSI" to the host computer). | |
32 | * Note that in the latter case, this code does not solve the | |
33 | * addressing issue (i.e. which SATA disk to address behind the logical | |
34 | * SCSI (RAID) interface). | |
35 | * | |
36 | */ | |
37 | ||
38 | #include <stdio.h> | |
39 | #include <string.h> | |
40 | ||
41 | #include "config.h" | |
42 | #include "int64.h" | |
43 | #include "extern.h" | |
44 | #include "scsicmds.h" | |
45 | #include "scsiata.h" | |
46 | #include "utility.h" | |
47 | ||
48 | const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.7 2006/08/09 20:40:19 chrfranke Exp $" | |
49 | CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID; | |
50 | ||
51 | /* for passing global control variables */ | |
52 | extern smartmonctrl *con; | |
53 | ||
54 | #define DEF_SAT_ATA_PASSTHRU_SIZE 16 | |
55 | ||
56 | ||
57 | // cdb[0]: ATA PASS THROUGH (16) SCSI command opcode byte (0x85) | |
58 | // cdb[1]: multiple_count, protocol + extend | |
59 | // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length | |
60 | // cdb[3]: features (15:8) | |
61 | // cdb[4]: features (7:0) | |
62 | // cdb[5]: sector_count (15:8) | |
63 | // cdb[6]: sector_count (7:0) | |
64 | // cdb[7]: lba_low (15:8) | |
65 | // cdb[8]: lba_low (7:0) | |
66 | // cdb[9]: lba_mid (15:8) | |
67 | // cdb[10]: lba_mid (7:0) | |
68 | // cdb[11]: lba_high (15:8) | |
69 | // cdb[12]: lba_high (7:0) | |
70 | // cdb[13]: device | |
71 | // cdb[14]: (ata) command | |
72 | // cdb[15]: control (SCSI, leave as zero) | |
73 | // | |
74 | // 24 bit lba (from MSB): cdb[12] cdb[10] cdb[8] | |
75 | // 48 bit lba (from MSB): cdb[11] cdb[9] cdb[7] cdb[12] cdb[10] cdb[8] | |
76 | // | |
77 | // | |
78 | // cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1) | |
79 | // cdb[1]: multiple_count, protocol + extend | |
80 | // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length | |
81 | // cdb[3]: features (7:0) | |
82 | // cdb[4]: sector_count (7:0) | |
83 | // cdb[5]: lba_low (7:0) | |
84 | // cdb[6]: lba_mid (7:0) | |
85 | // cdb[7]: lba_high (7:0) | |
86 | // cdb[8]: device | |
87 | // cdb[9]: (ata) command | |
88 | // cdb[10]: reserved | |
89 | // cdb[11]: control (SCSI, leave as zero) | |
90 | // | |
91 | // | |
92 | // ATA status return descriptor (component of descriptor sense data) | |
93 | // des[0]: descriptor code (0x9) | |
94 | // des[1]: additional descriptor length (0xc) | |
95 | // des[2]: extend (bit 0) | |
96 | // des[3]: error | |
97 | // des[4]: sector_count (15:8) | |
98 | // des[5]: sector_count (7:0) | |
99 | // des[6]: lba_low (15:8) | |
100 | // des[7]: lba_low (7:0) | |
101 | // des[8]: lba_mid (15:8) | |
102 | // des[9]: lba_mid (7:0) | |
103 | // des[10]: lba_high (15:8) | |
104 | // des[11]: lba_high (7:0) | |
105 | // des[12]: device | |
106 | // des[13]: status | |
107 | ||
108 | ||
109 | ||
110 | // PURPOSE | |
111 | // This interface routine takes ATA SMART commands and packages | |
112 | // them in the SAT-defined ATA PASS THROUGH SCSI commands. There are | |
113 | // two available SCSI commands: a 12 byte and 16 byte variant; the | |
114 | // one used is chosen via con->satpassthrulen . | |
115 | // DETAILED DESCRIPTION OF ARGUMENTS | |
116 | // device: is the file descriptor provided by (a SCSI dvice type) open() | |
117 | // command: defines the different ATA operations. | |
118 | // select: additional input data if needed (which log, which type of | |
119 | // self-test). | |
120 | // data: location to write output data, if needed (512 bytes). | |
121 | // Note: not all commands use all arguments. | |
122 | // RETURN VALUES | |
123 | // -1 if the command failed | |
124 | // 0 if the command succeeded, | |
125 | // STATUS_CHECK routine: | |
126 | // -1 if the command failed | |
127 | // 0 if the command succeeded and disk SMART status is "OK" | |
128 | // 1 if the command succeeded and disk SMART status is "FAILING" | |
129 | ||
130 | int sat_command_interface(int device, smart_command_set command, int select, | |
131 | char *data) | |
132 | { | |
133 | struct scsi_cmnd_io io_hdr; | |
134 | struct scsi_sense_disect sinfo; | |
135 | struct sg_scsi_sense_hdr ssh; | |
136 | unsigned char cdb[SAT_ATA_PASSTHROUGH_16LEN]; | |
137 | unsigned char sense[32]; | |
138 | const unsigned char * ucp; | |
139 | int status, len; | |
140 | int copydata = 0; | |
141 | int outlen = 0; | |
142 | int extend = 0; | |
143 | int chk_cond = 0; /* set to 1 to read register(s) back */ | |
144 | int protocol = 3; /* non-data */ | |
145 | int t_dir = 1; /* 0 -> to device, 1 -> from device */ | |
146 | int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ | |
147 | int t_length = 0; /* 0 -> no data transferred */ | |
148 | int feature = 0; | |
149 | int ata_command = 0; | |
150 | int sector_count = 0; | |
151 | int lba_low = 0; | |
152 | int lba_mid = 0; | |
153 | int lba_high = 0; | |
154 | int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE; | |
155 | ||
156 | memset(cdb, 0, sizeof(cdb)); | |
157 | memset(sense, 0, sizeof(sense)); | |
158 | ||
159 | ata_command = ATA_SMART_CMD; | |
160 | switch (command) { | |
161 | case CHECK_POWER_MODE: | |
162 | ata_command = ATA_CHECK_POWER_MODE; | |
163 | chk_cond = 1; | |
164 | copydata = 1; | |
165 | break; | |
166 | case READ_VALUES: /* READ DATA */ | |
167 | feature = ATA_SMART_READ_VALUES; | |
168 | sector_count = 1; /* one (512 byte) block */ | |
169 | protocol = 4; /* PIO data-in */ | |
170 | t_length = 2; /* sector count holds count */ | |
171 | copydata = 512; | |
172 | break; | |
173 | case READ_THRESHOLDS: /* obsolete */ | |
174 | feature = ATA_SMART_READ_THRESHOLDS; | |
175 | sector_count = 1; /* one (512 byte) block */ | |
176 | lba_low = 1; | |
177 | protocol = 4; /* PIO data-in */ | |
178 | t_length = 2; /* sector count holds count */ | |
179 | copydata=512; | |
180 | break; | |
181 | case READ_LOG: | |
182 | feature = ATA_SMART_READ_LOG_SECTOR; | |
183 | sector_count = 1; /* one (512 byte) block */ | |
184 | lba_low = select; | |
185 | protocol = 4; /* PIO data-in */ | |
186 | t_length = 2; /* sector count holds count */ | |
187 | copydata = 512; | |
188 | break; | |
189 | case WRITE_LOG: | |
190 | feature = ATA_SMART_WRITE_LOG_SECTOR; | |
191 | sector_count = 1; /* one (512 byte) block */ | |
192 | lba_low = select; | |
193 | protocol = 5; /* PIO data-out */ | |
194 | t_length = 2; /* sector count holds count */ | |
195 | t_dir = 0; /* to device */ | |
196 | outlen = 512; | |
197 | break; | |
198 | case IDENTIFY: | |
199 | ata_command = ATA_IDENTIFY_DEVICE; | |
200 | sector_count = 1; /* one (512 byte) block */ | |
201 | protocol = 4; /* PIO data-in */ | |
202 | t_length = 2; /* sector count holds count */ | |
203 | copydata = 512; | |
204 | break; | |
205 | case PIDENTIFY: | |
206 | ata_command = ATA_IDENTIFY_PACKET_DEVICE; | |
207 | sector_count = 1; /* one (512 byte) block */ | |
208 | protocol = 4; /* PIO data-in */ | |
209 | t_length = 2; /* sector count (7:0) holds count */ | |
210 | copydata = 512; | |
211 | break; | |
212 | case ENABLE: | |
213 | feature = ATA_SMART_ENABLE; | |
214 | lba_low = 1; | |
215 | break; | |
216 | case DISABLE: | |
217 | feature = ATA_SMART_DISABLE; | |
218 | lba_low = 1; | |
219 | break; | |
220 | case STATUS: | |
221 | // this command only says if SMART is working. It could be | |
222 | // replaced with STATUS_CHECK below. | |
223 | feature = ATA_SMART_STATUS; | |
224 | chk_cond = 1; | |
225 | break; | |
226 | case AUTO_OFFLINE: | |
227 | feature = ATA_SMART_AUTO_OFFLINE; | |
228 | sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! | |
229 | break; | |
230 | case AUTOSAVE: | |
231 | feature = ATA_SMART_AUTOSAVE; | |
232 | sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! | |
233 | break; | |
234 | case IMMEDIATE_OFFLINE: | |
235 | feature = ATA_SMART_IMMEDIATE_OFFLINE; | |
236 | lba_low = select; | |
237 | break; | |
238 | case STATUS_CHECK: | |
239 | // This command uses HDIO_DRIVE_TASK and has different syntax than | |
240 | // the other commands. | |
241 | feature = ATA_SMART_STATUS; /* SMART RETURN STATUS */ | |
242 | chk_cond = 1; | |
243 | break; | |
244 | default: | |
245 | pout("Unrecognized command %d in sat_command_interface()\n" | |
246 | "Please contact " PACKAGE_BUGREPORT "\n", command); | |
247 | errno=ENOSYS; | |
248 | return -1; | |
249 | } | |
250 | if (ATA_SMART_CMD == ata_command) { | |
251 | lba_mid = 0x4f; | |
252 | lba_high = 0xc2; | |
253 | } | |
254 | ||
255 | if ((SAT_ATA_PASSTHROUGH_12LEN == con->satpassthrulen) || | |
256 | (SAT_ATA_PASSTHROUGH_16LEN == con->satpassthrulen)) | |
257 | passthru_size = con->satpassthrulen; | |
258 | cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? | |
259 | SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16; | |
260 | ||
261 | cdb[1] = (protocol << 1) | extend; | |
262 | cdb[2] = (chk_cond << 5) | (t_dir << 3) | | |
263 | (byte_block << 2) | t_length; | |
264 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 3 : 4] = feature; | |
265 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 4 : 6] = sector_count; | |
266 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 5 : 8] = lba_low; | |
267 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 6 : 10] = lba_mid; | |
268 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 7 : 12] = lba_high; | |
269 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 9 : 14] = ata_command; | |
270 | ||
271 | memset(&io_hdr, 0, sizeof(io_hdr)); | |
272 | if (0 == t_length) { | |
273 | io_hdr.dxfer_dir = DXFER_NONE; | |
274 | io_hdr.dxfer_len = 0; | |
275 | } else if (t_dir) { /* from device */ | |
276 | io_hdr.dxfer_dir = DXFER_FROM_DEVICE; | |
277 | io_hdr.dxfer_len = copydata; | |
278 | io_hdr.dxferp = (unsigned char *)data; | |
279 | memset(data, 0, copydata); /* prefill with zeroes */ | |
280 | } else { /* to device */ | |
281 | io_hdr.dxfer_dir = DXFER_TO_DEVICE; | |
282 | io_hdr.dxfer_len = outlen; | |
283 | io_hdr.dxferp = (unsigned char *)data; | |
284 | } | |
285 | io_hdr.cmnd = cdb; | |
286 | io_hdr.cmnd_len = passthru_size; | |
287 | io_hdr.sensep = sense; | |
288 | io_hdr.max_sense_len = sizeof(sense); | |
289 | io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; | |
290 | ||
291 | status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); | |
292 | if (0 != status) { | |
293 | if (con->reportscsiioctl > 0) | |
294 | pout("sat_command_interface: do_scsi_cmnd_io() failed, " | |
295 | "status=%d\n", status); | |
296 | return -1; | |
297 | } | |
298 | if (chk_cond) { /* expecting SAT specific sense data */ | |
299 | ucp = NULL; | |
300 | if (sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, | |
301 | &ssh)) { | |
302 | /* look for SAT extended ATA status return descriptor (9) */ | |
303 | ucp = sg_scsi_sense_desc_find(io_hdr.sensep, | |
304 | io_hdr.resp_sense_len, 9); | |
305 | if (ucp) { | |
306 | len = ucp[1] + 2; | |
307 | if (len < 12) | |
308 | len = 12; | |
309 | else if (len > 14) | |
310 | len = 14; | |
311 | if (con->reportscsiioctl > 1) { | |
312 | pout("Values from ATA status return descriptor are:\n"); | |
313 | dStrHex((const char *)ucp, len, 1); | |
314 | } | |
315 | if (ATA_CHECK_POWER_MODE == cdb[14]) | |
316 | data[0] = ucp[5]; /* sector count (0:7) */ | |
317 | else if (STATUS_CHECK == command) { | |
318 | if ((ucp[9] == 0x4f) && (ucp[11] == 0xc2)) | |
319 | return 0; /* GOOD smart status */ | |
320 | if ((ucp[9] == 0xf4) && (ucp[11] == 0x2c)) | |
321 | return 1; // smart predicting failure, "bad" status | |
322 | // We haven't gotten output that makes sense so | |
323 | // print out some debugging info | |
324 | syserror("Error SMART Status command failed"); | |
325 | pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); | |
326 | pout("Values from ATA status return descriptor are:\n"); | |
327 | dStrHex((const char *)ucp, len, 1); | |
328 | return -1; | |
329 | } | |
330 | } | |
331 | } | |
332 | if (ucp == NULL) { | |
333 | chk_cond = 0; /* not the type of sense data expected */ | |
334 | if (t_dir && (t_length > 0)) | |
335 | data[0] = 0; | |
336 | } | |
337 | } | |
338 | if (0 == chk_cond) { | |
339 | ucp = NULL; | |
340 | if (sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, | |
341 | &ssh)) { | |
342 | if ((ssh.response_code >= 0x72) && | |
343 | ((SCSI_SK_NO_SENSE == ssh.sense_key) || | |
344 | (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) && | |
345 | (0 == ssh.asc) && | |
346 | (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) { | |
347 | /* look for SAT extended ATA status return descriptor (9) */ | |
348 | ucp = sg_scsi_sense_desc_find(io_hdr.sensep, | |
349 | io_hdr.resp_sense_len, 9); | |
350 | if (ucp) { | |
351 | if (con->reportscsiioctl > 0) { | |
352 | pout("Values from ATA status return descriptor are:\n"); | |
353 | len = ucp[1] + 2; | |
354 | if (len < 12) | |
355 | len = 12; | |
356 | else if (len > 14) | |
357 | len = 14; | |
358 | dStrHex((const char *)ucp, len, 1); | |
359 | } | |
360 | return -1; | |
361 | } | |
362 | } | |
363 | scsi_do_sense_disect(&io_hdr, &sinfo); | |
364 | status = scsiSimpleSenseFilter(&sinfo); | |
365 | if (0 != status) { | |
366 | if (con->reportscsiioctl > 0) | |
367 | pout("sat_command_interface: scsi error: %s\n", | |
368 | scsiErrString(status)); | |
369 | return -1; | |
370 | } | |
371 | } | |
372 | } | |
373 | return 0; | |
374 | } | |
375 | ||
376 | /* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface | |
377 | is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful | |
378 | return 1, else 0 */ | |
379 | int has_sat_pass_through(int device, int packet_interface) | |
380 | { | |
381 | char data[512]; | |
382 | smart_command_set command; | |
383 | ||
384 | command = packet_interface ? PIDENTIFY : IDENTIFY; | |
385 | if (0 == sat_command_interface(device, command, 0, data)) | |
386 | return 1; | |
387 | else | |
388 | return 0; | |
389 | } | |
390 | ||
391 | /* Next two functions are borrowed from sg_lib.c in the sg3_utils | |
392 | package. Same copyrght owner, same license as this file. */ | |
393 | int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, | |
394 | struct sg_scsi_sense_hdr * sshp) | |
395 | { | |
396 | if (sshp) | |
397 | memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); | |
398 | if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0]))) | |
399 | return 0; | |
400 | if (sshp) { | |
401 | sshp->response_code = (0x7f & sensep[0]); | |
402 | if (sshp->response_code >= 0x72) { /* descriptor format */ | |
403 | if (sb_len > 1) | |
404 | sshp->sense_key = (0xf & sensep[1]); | |
405 | if (sb_len > 2) | |
406 | sshp->asc = sensep[2]; | |
407 | if (sb_len > 3) | |
408 | sshp->ascq = sensep[3]; | |
409 | if (sb_len > 7) | |
410 | sshp->additional_length = sensep[7]; | |
411 | } else { /* fixed format */ | |
412 | if (sb_len > 2) | |
413 | sshp->sense_key = (0xf & sensep[2]); | |
414 | if (sb_len > 7) { | |
415 | sb_len = (sb_len < (sensep[7] + 8)) ? sb_len : | |
416 | (sensep[7] + 8); | |
417 | if (sb_len > 12) | |
418 | sshp->asc = sensep[12]; | |
419 | if (sb_len > 13) | |
420 | sshp->ascq = sensep[13]; | |
421 | } | |
422 | } | |
423 | } | |
424 | return 1; | |
425 | } | |
426 | ||
427 | ||
428 | const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, | |
429 | int sense_len, int desc_type) | |
430 | { | |
431 | int add_sen_len, add_len, desc_len, k; | |
432 | const unsigned char * descp; | |
433 | ||
434 | if ((sense_len < 8) || (0 == (add_sen_len = sensep[7]))) | |
435 | return NULL; | |
436 | if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) | |
437 | return NULL; | |
438 | add_sen_len = (add_sen_len < (sense_len - 8)) ? | |
439 | add_sen_len : (sense_len - 8); | |
440 | descp = &sensep[8]; | |
441 | for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { | |
442 | descp += desc_len; | |
443 | add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; | |
444 | desc_len = add_len + 2; | |
445 | if (descp[0] == desc_type) | |
446 | return descp; | |
447 | if (add_len < 0) /* short descriptor ?? */ | |
448 | break; | |
449 | } | |
450 | return NULL; | |
451 | } |