]>
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 | ||
1953ff6d | 48 | const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.8 2007/12/03 02:14:20 dpgilbert Exp $" |
4d59bff9 GG |
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 | |
1953ff6d | 55 | #define ATA_RETURN_DESCRIPTOR 9 |
4d59bff9 GG |
56 | |
57 | ||
58 | // cdb[0]: ATA PASS THROUGH (16) SCSI command opcode byte (0x85) | |
59 | // cdb[1]: multiple_count, protocol + extend | |
60 | // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length | |
61 | // cdb[3]: features (15:8) | |
62 | // cdb[4]: features (7:0) | |
63 | // cdb[5]: sector_count (15:8) | |
64 | // cdb[6]: sector_count (7:0) | |
65 | // cdb[7]: lba_low (15:8) | |
66 | // cdb[8]: lba_low (7:0) | |
67 | // cdb[9]: lba_mid (15:8) | |
68 | // cdb[10]: lba_mid (7:0) | |
69 | // cdb[11]: lba_high (15:8) | |
70 | // cdb[12]: lba_high (7:0) | |
71 | // cdb[13]: device | |
72 | // cdb[14]: (ata) command | |
73 | // cdb[15]: control (SCSI, leave as zero) | |
74 | // | |
75 | // 24 bit lba (from MSB): cdb[12] cdb[10] cdb[8] | |
76 | // 48 bit lba (from MSB): cdb[11] cdb[9] cdb[7] cdb[12] cdb[10] cdb[8] | |
77 | // | |
78 | // | |
79 | // cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1) | |
80 | // cdb[1]: multiple_count, protocol + extend | |
81 | // cdb[2]: offline, ck_cond, t_dir, byte_block + t_length | |
82 | // cdb[3]: features (7:0) | |
83 | // cdb[4]: sector_count (7:0) | |
84 | // cdb[5]: lba_low (7:0) | |
85 | // cdb[6]: lba_mid (7:0) | |
86 | // cdb[7]: lba_high (7:0) | |
87 | // cdb[8]: device | |
88 | // cdb[9]: (ata) command | |
89 | // cdb[10]: reserved | |
90 | // cdb[11]: control (SCSI, leave as zero) | |
91 | // | |
92 | // | |
1953ff6d | 93 | // ATA Return Descriptor (component of descriptor sense data) |
4d59bff9 GG |
94 | // des[0]: descriptor code (0x9) |
95 | // des[1]: additional descriptor length (0xc) | |
96 | // des[2]: extend (bit 0) | |
97 | // des[3]: error | |
98 | // des[4]: sector_count (15:8) | |
99 | // des[5]: sector_count (7:0) | |
100 | // des[6]: lba_low (15:8) | |
101 | // des[7]: lba_low (7:0) | |
102 | // des[8]: lba_mid (15:8) | |
103 | // des[9]: lba_mid (7:0) | |
104 | // des[10]: lba_high (15:8) | |
105 | // des[11]: lba_high (7:0) | |
106 | // des[12]: device | |
107 | // des[13]: status | |
108 | ||
109 | ||
110 | ||
111 | // PURPOSE | |
112 | // This interface routine takes ATA SMART commands and packages | |
113 | // them in the SAT-defined ATA PASS THROUGH SCSI commands. There are | |
114 | // two available SCSI commands: a 12 byte and 16 byte variant; the | |
115 | // one used is chosen via con->satpassthrulen . | |
116 | // DETAILED DESCRIPTION OF ARGUMENTS | |
117 | // device: is the file descriptor provided by (a SCSI dvice type) open() | |
118 | // command: defines the different ATA operations. | |
119 | // select: additional input data if needed (which log, which type of | |
120 | // self-test). | |
121 | // data: location to write output data, if needed (512 bytes). | |
122 | // Note: not all commands use all arguments. | |
123 | // RETURN VALUES | |
124 | // -1 if the command failed | |
125 | // 0 if the command succeeded, | |
126 | // STATUS_CHECK routine: | |
127 | // -1 if the command failed | |
128 | // 0 if the command succeeded and disk SMART status is "OK" | |
129 | // 1 if the command succeeded and disk SMART status is "FAILING" | |
130 | ||
131 | int sat_command_interface(int device, smart_command_set command, int select, | |
132 | char *data) | |
133 | { | |
134 | struct scsi_cmnd_io io_hdr; | |
135 | struct scsi_sense_disect sinfo; | |
136 | struct sg_scsi_sense_hdr ssh; | |
137 | unsigned char cdb[SAT_ATA_PASSTHROUGH_16LEN]; | |
138 | unsigned char sense[32]; | |
1953ff6d GG |
139 | const unsigned char * ardp; |
140 | int status, ard_len, have_sense; | |
4d59bff9 GG |
141 | int copydata = 0; |
142 | int outlen = 0; | |
143 | int extend = 0; | |
1953ff6d | 144 | int ck_cond = 0; /* set to 1 to read register(s) back */ |
4d59bff9 GG |
145 | int protocol = 3; /* non-data */ |
146 | int t_dir = 1; /* 0 -> to device, 1 -> from device */ | |
147 | int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */ | |
148 | int t_length = 0; /* 0 -> no data transferred */ | |
149 | int feature = 0; | |
150 | int ata_command = 0; | |
151 | int sector_count = 0; | |
152 | int lba_low = 0; | |
153 | int lba_mid = 0; | |
154 | int lba_high = 0; | |
155 | int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE; | |
156 | ||
157 | memset(cdb, 0, sizeof(cdb)); | |
158 | memset(sense, 0, sizeof(sense)); | |
159 | ||
160 | ata_command = ATA_SMART_CMD; | |
161 | switch (command) { | |
162 | case CHECK_POWER_MODE: | |
163 | ata_command = ATA_CHECK_POWER_MODE; | |
1953ff6d | 164 | ck_cond = 1; |
4d59bff9 GG |
165 | copydata = 1; |
166 | break; | |
167 | case READ_VALUES: /* READ DATA */ | |
168 | feature = ATA_SMART_READ_VALUES; | |
169 | sector_count = 1; /* one (512 byte) block */ | |
170 | protocol = 4; /* PIO data-in */ | |
171 | t_length = 2; /* sector count holds count */ | |
172 | copydata = 512; | |
173 | break; | |
174 | case READ_THRESHOLDS: /* obsolete */ | |
175 | feature = ATA_SMART_READ_THRESHOLDS; | |
176 | sector_count = 1; /* one (512 byte) block */ | |
177 | lba_low = 1; | |
178 | protocol = 4; /* PIO data-in */ | |
179 | t_length = 2; /* sector count holds count */ | |
180 | copydata=512; | |
181 | break; | |
182 | case READ_LOG: | |
183 | feature = ATA_SMART_READ_LOG_SECTOR; | |
184 | sector_count = 1; /* one (512 byte) block */ | |
185 | lba_low = select; | |
186 | protocol = 4; /* PIO data-in */ | |
187 | t_length = 2; /* sector count holds count */ | |
188 | copydata = 512; | |
189 | break; | |
190 | case WRITE_LOG: | |
191 | feature = ATA_SMART_WRITE_LOG_SECTOR; | |
192 | sector_count = 1; /* one (512 byte) block */ | |
193 | lba_low = select; | |
194 | protocol = 5; /* PIO data-out */ | |
195 | t_length = 2; /* sector count holds count */ | |
196 | t_dir = 0; /* to device */ | |
197 | outlen = 512; | |
198 | break; | |
199 | case IDENTIFY: | |
200 | ata_command = ATA_IDENTIFY_DEVICE; | |
201 | sector_count = 1; /* one (512 byte) block */ | |
202 | protocol = 4; /* PIO data-in */ | |
203 | t_length = 2; /* sector count holds count */ | |
204 | copydata = 512; | |
205 | break; | |
206 | case PIDENTIFY: | |
207 | ata_command = ATA_IDENTIFY_PACKET_DEVICE; | |
208 | sector_count = 1; /* one (512 byte) block */ | |
209 | protocol = 4; /* PIO data-in */ | |
210 | t_length = 2; /* sector count (7:0) holds count */ | |
211 | copydata = 512; | |
212 | break; | |
213 | case ENABLE: | |
214 | feature = ATA_SMART_ENABLE; | |
215 | lba_low = 1; | |
216 | break; | |
217 | case DISABLE: | |
218 | feature = ATA_SMART_DISABLE; | |
219 | lba_low = 1; | |
220 | break; | |
221 | case STATUS: | |
222 | // this command only says if SMART is working. It could be | |
223 | // replaced with STATUS_CHECK below. | |
224 | feature = ATA_SMART_STATUS; | |
1953ff6d | 225 | ck_cond = 1; |
4d59bff9 GG |
226 | break; |
227 | case AUTO_OFFLINE: | |
228 | feature = ATA_SMART_AUTO_OFFLINE; | |
229 | sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! | |
230 | break; | |
231 | case AUTOSAVE: | |
232 | feature = ATA_SMART_AUTOSAVE; | |
233 | sector_count = select; // YET NOTE - THIS IS A NON-DATA COMMAND!! | |
234 | break; | |
235 | case IMMEDIATE_OFFLINE: | |
236 | feature = ATA_SMART_IMMEDIATE_OFFLINE; | |
237 | lba_low = select; | |
238 | break; | |
239 | case STATUS_CHECK: | |
240 | // This command uses HDIO_DRIVE_TASK and has different syntax than | |
241 | // the other commands. | |
242 | feature = ATA_SMART_STATUS; /* SMART RETURN STATUS */ | |
1953ff6d | 243 | ck_cond = 1; |
4d59bff9 GG |
244 | break; |
245 | default: | |
246 | pout("Unrecognized command %d in sat_command_interface()\n" | |
247 | "Please contact " PACKAGE_BUGREPORT "\n", command); | |
248 | errno=ENOSYS; | |
249 | return -1; | |
250 | } | |
251 | if (ATA_SMART_CMD == ata_command) { | |
252 | lba_mid = 0x4f; | |
253 | lba_high = 0xc2; | |
254 | } | |
255 | ||
256 | if ((SAT_ATA_PASSTHROUGH_12LEN == con->satpassthrulen) || | |
257 | (SAT_ATA_PASSTHROUGH_16LEN == con->satpassthrulen)) | |
258 | passthru_size = con->satpassthrulen; | |
259 | cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? | |
260 | SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16; | |
261 | ||
262 | cdb[1] = (protocol << 1) | extend; | |
1953ff6d | 263 | cdb[2] = (ck_cond << 5) | (t_dir << 3) | |
4d59bff9 GG |
264 | (byte_block << 2) | t_length; |
265 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 3 : 4] = feature; | |
266 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 4 : 6] = sector_count; | |
267 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 5 : 8] = lba_low; | |
268 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 6 : 10] = lba_mid; | |
269 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 7 : 12] = lba_high; | |
270 | cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 9 : 14] = ata_command; | |
271 | ||
272 | memset(&io_hdr, 0, sizeof(io_hdr)); | |
273 | if (0 == t_length) { | |
274 | io_hdr.dxfer_dir = DXFER_NONE; | |
275 | io_hdr.dxfer_len = 0; | |
276 | } else if (t_dir) { /* from device */ | |
277 | io_hdr.dxfer_dir = DXFER_FROM_DEVICE; | |
278 | io_hdr.dxfer_len = copydata; | |
279 | io_hdr.dxferp = (unsigned char *)data; | |
280 | memset(data, 0, copydata); /* prefill with zeroes */ | |
281 | } else { /* to device */ | |
282 | io_hdr.dxfer_dir = DXFER_TO_DEVICE; | |
283 | io_hdr.dxfer_len = outlen; | |
284 | io_hdr.dxferp = (unsigned char *)data; | |
285 | } | |
286 | io_hdr.cmnd = cdb; | |
287 | io_hdr.cmnd_len = passthru_size; | |
288 | io_hdr.sensep = sense; | |
289 | io_hdr.max_sense_len = sizeof(sense); | |
290 | io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; | |
291 | ||
292 | status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl); | |
293 | if (0 != status) { | |
294 | if (con->reportscsiioctl > 0) | |
295 | pout("sat_command_interface: do_scsi_cmnd_io() failed, " | |
296 | "status=%d\n", status); | |
297 | return -1; | |
298 | } | |
1953ff6d GG |
299 | ardp = NULL; |
300 | ard_len = 0; | |
301 | have_sense = sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, | |
302 | &ssh); | |
303 | if (have_sense) { | |
304 | /* look for SAT ATA Return Descriptor */ | |
305 | ardp = sg_scsi_sense_desc_find(io_hdr.sensep, | |
306 | io_hdr.resp_sense_len, | |
307 | ATA_RETURN_DESCRIPTOR); | |
308 | if (ardp) { | |
309 | ard_len = ardp[1] + 2; | |
310 | if (ard_len < 12) | |
311 | ard_len = 12; | |
312 | else if (ard_len > 14) | |
313 | ard_len = 14; | |
314 | } | |
315 | scsi_do_sense_disect(&io_hdr, &sinfo); | |
316 | status = scsiSimpleSenseFilter(&sinfo); | |
317 | if (0 != status) { | |
318 | if (con->reportscsiioctl > 0) { | |
319 | pout("sat_command_interface: scsi error: %s\n", | |
320 | scsiErrString(status)); | |
321 | if (ardp && (con->reportscsiioctl > 1)) { | |
322 | pout("Values from ATA Return Descriptor are:\n"); | |
323 | dStrHex((const char *)ardp, ard_len, 1); | |
324 | } | |
325 | } | |
326 | if (t_dir && (t_length > 0) && (copydata > 0)) | |
327 | memset(data, 0, copydata); | |
328 | return -1; | |
329 | } | |
330 | } | |
331 | if (ck_cond) { /* expecting SAT specific sense data */ | |
332 | if (have_sense) { | |
333 | if (ardp) { | |
4d59bff9 | 334 | if (con->reportscsiioctl > 1) { |
1953ff6d GG |
335 | pout("Values from ATA Return Descriptor are:\n"); |
336 | dStrHex((const char *)ardp, ard_len, 1); | |
4d59bff9 | 337 | } |
1953ff6d GG |
338 | if (ATA_CHECK_POWER_MODE == ata_command) |
339 | data[0] = ardp[5]; /* sector count (0:7) */ | |
4d59bff9 | 340 | else if (STATUS_CHECK == command) { |
1953ff6d | 341 | if ((ardp[9] == 0x4f) && (ardp[11] == 0xc2)) |
4d59bff9 | 342 | return 0; /* GOOD smart status */ |
1953ff6d | 343 | if ((ardp[9] == 0xf4) && (ardp[11] == 0x2c)) |
4d59bff9 GG |
344 | return 1; // smart predicting failure, "bad" status |
345 | // We haven't gotten output that makes sense so | |
346 | // print out some debugging info | |
347 | syserror("Error SMART Status command failed"); | |
348 | pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); | |
1953ff6d GG |
349 | pout("Values from ATA Return Descriptor are:\n"); |
350 | dStrHex((const char *)ardp, ard_len, 1); | |
4d59bff9 GG |
351 | return -1; |
352 | } | |
353 | } | |
354 | } | |
1953ff6d GG |
355 | if (ardp == NULL) |
356 | ck_cond = 0; /* not the type of sense data expected */ | |
4d59bff9 | 357 | } |
1953ff6d GG |
358 | if (0 == ck_cond) { |
359 | if (have_sense) { | |
4d59bff9 GG |
360 | if ((ssh.response_code >= 0x72) && |
361 | ((SCSI_SK_NO_SENSE == ssh.sense_key) || | |
362 | (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) && | |
363 | (0 == ssh.asc) && | |
364 | (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) { | |
1953ff6d | 365 | if (ardp) { |
4d59bff9 | 366 | if (con->reportscsiioctl > 0) { |
1953ff6d GG |
367 | pout("Values from ATA Return Descriptor are:\n"); |
368 | dStrHex((const char *)ardp, ard_len, 1); | |
4d59bff9 GG |
369 | } |
370 | return -1; | |
371 | } | |
372 | } | |
4d59bff9 GG |
373 | } |
374 | } | |
375 | return 0; | |
376 | } | |
377 | ||
378 | /* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface | |
379 | is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful | |
380 | return 1, else 0 */ | |
381 | int has_sat_pass_through(int device, int packet_interface) | |
382 | { | |
383 | char data[512]; | |
384 | smart_command_set command; | |
385 | ||
386 | command = packet_interface ? PIDENTIFY : IDENTIFY; | |
387 | if (0 == sat_command_interface(device, command, 0, data)) | |
388 | return 1; | |
389 | else | |
390 | return 0; | |
391 | } | |
392 | ||
393 | /* Next two functions are borrowed from sg_lib.c in the sg3_utils | |
394 | package. Same copyrght owner, same license as this file. */ | |
395 | int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len, | |
396 | struct sg_scsi_sense_hdr * sshp) | |
397 | { | |
398 | if (sshp) | |
399 | memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr)); | |
400 | if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0]))) | |
401 | return 0; | |
402 | if (sshp) { | |
403 | sshp->response_code = (0x7f & sensep[0]); | |
404 | if (sshp->response_code >= 0x72) { /* descriptor format */ | |
405 | if (sb_len > 1) | |
406 | sshp->sense_key = (0xf & sensep[1]); | |
407 | if (sb_len > 2) | |
408 | sshp->asc = sensep[2]; | |
409 | if (sb_len > 3) | |
410 | sshp->ascq = sensep[3]; | |
411 | if (sb_len > 7) | |
412 | sshp->additional_length = sensep[7]; | |
413 | } else { /* fixed format */ | |
414 | if (sb_len > 2) | |
415 | sshp->sense_key = (0xf & sensep[2]); | |
416 | if (sb_len > 7) { | |
417 | sb_len = (sb_len < (sensep[7] + 8)) ? sb_len : | |
418 | (sensep[7] + 8); | |
419 | if (sb_len > 12) | |
420 | sshp->asc = sensep[12]; | |
421 | if (sb_len > 13) | |
422 | sshp->ascq = sensep[13]; | |
423 | } | |
424 | } | |
425 | } | |
426 | return 1; | |
427 | } | |
428 | ||
429 | ||
430 | const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, | |
431 | int sense_len, int desc_type) | |
432 | { | |
433 | int add_sen_len, add_len, desc_len, k; | |
434 | const unsigned char * descp; | |
435 | ||
436 | if ((sense_len < 8) || (0 == (add_sen_len = sensep[7]))) | |
437 | return NULL; | |
438 | if ((sensep[0] < 0x72) || (sensep[0] > 0x73)) | |
439 | return NULL; | |
440 | add_sen_len = (add_sen_len < (sense_len - 8)) ? | |
441 | add_sen_len : (sense_len - 8); | |
442 | descp = &sensep[8]; | |
443 | for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { | |
444 | descp += desc_len; | |
445 | add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; | |
446 | desc_len = add_len + 2; | |
447 | if (descp[0] == desc_type) | |
448 | return descp; | |
449 | if (add_len < 0) /* short descriptor ?? */ | |
450 | break; | |
451 | } | |
452 | return NULL; | |
453 | } |