smartmontools  SVN Rev 5304
Utility to control and monitor storage systems with "S.M.A.R.T."
dev_jmb39x_raid.cpp
Go to the documentation of this file.
1 /*
2  * dev_jmb39x_raid.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2019-21 Christian Franke
7  *
8  * Based on JMraidcon (same license):
9  * Copyright (C) 2010 Werner Johansson
10  * http://git.xnk.nu/?p=JMraidcon.git
11  * https://github.com/Vlad1mir-D/JMraidcon
12  *
13  * SPDX-License-Identifier: GPL-2.0-or-later
14  */
15 
16 #include "config.h"
17 
18 #include "dev_interface.h"
19 #include "dev_tunnelled.h"
20 #include "atacmds.h"
21 #include "scsicmds.h"
22 #include "sg_unaligned.h"
23 #include "static_assert.h"
24 #include "utility.h"
25 
26 #include <errno.h>
27 
28 const char * dev_jmb39x_raid_cpp_svnid = "$Id: dev_jmb39x_raid.cpp 5198 2021-02-01 20:36:02Z chrfranke $";
29 
30 static void jmbassert_failed(int line, const char * expr)
31 {
32  char msg[128];
33  // Avoid __FILE__ as it may break reproducible builds
34  snprintf(msg, sizeof(msg), "dev_jmb39x_raid.cpp(%d): Assertion failed: %s", line, expr);
35  throw std::logic_error(msg);
36 }
37 
38 #define jmbassert(expr) (!(expr) ? jmbassert_failed(__LINE__, #expr) : (void)0)
39 
40 static void jmb_xor(uint8_t (& data)[512])
41 {
42  static const uint8_t xor_table[] = {
43  0x08, 0xc1, 0x67, 0x44, 0x04, 0x91, 0x0d, 0x3d, 0x9c, 0x44, 0xdb, 0x61, 0xba, 0x63, 0x00, 0x5c,
44  0x48, 0x78, 0xc4, 0x19, 0x9f, 0xc8, 0x8a, 0x1f, 0x8f, 0xa3, 0x7f, 0x83, 0x08, 0xcf, 0x7a, 0x71,
45  0x89, 0xa4, 0x1d, 0xcd, 0xe7, 0xd2, 0x32, 0xe1, 0x27, 0xad, 0xd4, 0xfa, 0x0e, 0x03, 0x99, 0xeb,
46  0xf7, 0x83, 0x50, 0x50, 0x11, 0x2d, 0x79, 0xbe, 0x3c, 0xb4, 0xf1, 0xe3, 0x8f, 0xd9, 0x3b, 0x9f,
47  0xd9, 0xb0, 0xf3, 0x67, 0x87, 0x90, 0xe0, 0x5d, 0xff, 0xf9, 0xf0, 0x60, 0x61, 0x55, 0x1a, 0x2e,
48  0x81, 0x52, 0xaf, 0x73, 0xee, 0x25, 0xad, 0xc7, 0x01, 0x6e, 0xce, 0x6b, 0x01, 0x8d, 0x49, 0x74,
49  0x9c, 0x9e, 0xed, 0x7e, 0xe9, 0x3b, 0xf3, 0xa2, 0x8e, 0x45, 0xa0, 0x39, 0x0f, 0xcd, 0x96, 0x6b,
50  0x90, 0x3c, 0xa7, 0xb4, 0x5a, 0x6f, 0x72, 0xba, 0x08, 0x6b, 0x58, 0x1f, 0x35, 0x42, 0x2a, 0xc6,
51  0x4f, 0xf4, 0x51, 0xa2, 0xa1, 0x48, 0x6e, 0x89, 0xe9, 0x36, 0x6d, 0xc8, 0x3b, 0x12, 0xec, 0x3a,
52  0xad, 0x89, 0x2f, 0x37, 0xab, 0x1a, 0xde, 0x63, 0x2f, 0xef, 0x74, 0xee, 0xc7, 0xa9, 0x51, 0xd1,
53  0xae, 0x63, 0xad, 0x92, 0x1b, 0x78, 0x98, 0xf1, 0xb6, 0x40, 0xbb, 0xfa, 0x22, 0x07, 0xf3, 0x22,
54  0x95, 0xb7, 0x46, 0xa3, 0xca, 0x2b, 0x16, 0x85, 0x40, 0x41, 0x0a, 0xc5, 0xf3, 0x61, 0xc7, 0xad,
55  0x53, 0xfb, 0x1b, 0x65, 0xac, 0xc9, 0x55, 0xee, 0x73, 0xc1, 0x02, 0xa0, 0x29, 0xfe, 0x53, 0x15,
56  0x8f, 0x1f, 0xad, 0x8d, 0x77, 0xde, 0x15, 0xef, 0x6b, 0xf3, 0x1b, 0xd8, 0x44, 0x96, 0xe3, 0xaa,
57  0x5a, 0x2a, 0xdc, 0x10, 0x7b, 0x96, 0xda, 0x3c, 0x8b, 0xf2, 0x3d, 0x38, 0xa4, 0x81, 0xf3, 0x2c,
58  0x58, 0x41, 0xf5, 0x54, 0x73, 0x45, 0x9d, 0x73, 0xc5, 0xfd, 0xe8, 0x2a, 0xbe, 0xc6, 0x30, 0x50,
59  0x9e, 0x4f, 0x8f, 0xa0, 0x29, 0xed, 0x4a, 0xe9, 0x2f, 0x32, 0x03, 0xca, 0x13, 0xd8, 0x5b, 0x7a,
60  0xae, 0x9d, 0x58, 0xe6, 0x88, 0x73, 0x22, 0x90, 0x0a, 0x43, 0x6c, 0x41, 0x5b, 0x17, 0xc4, 0x1a,
61  0x27, 0x5e, 0xf9, 0xef, 0x63, 0x9f, 0x57, 0x23, 0x6c, 0x27, 0x97, 0x70, 0xf5, 0xa8, 0x5b, 0x7b,
62  0x5d, 0xa9, 0x0f, 0x37, 0xae, 0xff, 0x8b, 0xb2, 0xc8, 0xca, 0xd9, 0x28, 0x8e, 0x5b, 0xb2, 0x46,
63  0xbe, 0x80, 0x40, 0x38, 0xe4, 0xee, 0xbb, 0x2c, 0xd2, 0x82, 0xc1, 0x72, 0x5a, 0x11, 0x4f, 0x4b,
64  0x54, 0xe2, 0xb9, 0xf1, 0x24, 0x96, 0x53, 0x3d, 0x33, 0x81, 0xf1, 0x50, 0x2e, 0x1a, 0x04, 0x71,
65  0x80, 0xf9, 0xbf, 0x66, 0x69, 0x9c, 0x6f, 0x22, 0x44, 0xd0, 0x69, 0xbb, 0xad, 0x93, 0x84, 0x98,
66  0x74, 0xaf, 0x67, 0x32, 0xb9, 0x8f, 0x65, 0xf3, 0x4b, 0x0f, 0xf4, 0x85, 0xef, 0xb5, 0xba, 0xff,
67  0xe1, 0xda, 0x9e, 0x9e, 0x32, 0x96, 0xa9, 0x19, 0xb8, 0x4f, 0x43, 0xf7, 0xf6, 0x4c, 0x1c, 0x0f,
68  0xce, 0xd2, 0x67, 0xb6, 0xe3, 0xe3, 0x8d, 0x27, 0x1e, 0x27, 0x98, 0x4c, 0x73, 0x37, 0x5c, 0xff,
69  0xab, 0x16, 0xca, 0x64, 0x7d, 0x91, 0xc0, 0x6d, 0xae, 0x60, 0xf0, 0x1a, 0x43, 0x12, 0xe6, 0xf4,
70  0xd6, 0xe8, 0xba, 0xc2, 0x9b, 0x2f, 0xe6, 0xce, 0x07, 0x08, 0x6a, 0x8d, 0x28, 0x62, 0xa7, 0x31,
71  0xe9, 0x3d, 0x4b, 0x9b, 0x5b, 0x19, 0x18, 0x13, 0xd2, 0xa9, 0xc1, 0x08, 0xce, 0x62, 0x12, 0x8c,
72  0x12, 0x64, 0xe3, 0x43, 0xbb, 0xe3, 0x59, 0x1c, 0x57, 0x7f, 0xcd, 0xb9, 0x72, 0x65, 0x47, 0xab,
73  0xb8, 0xfe, 0x61, 0xc1, 0x08, 0xc2, 0xec, 0x25, 0x8e, 0xb9, 0x1c, 0x89, 0xdf, 0x6d, 0xd2, 0xa7,
74  0x36, 0xa7, 0x10, 0x52, 0x2a, 0x21, 0x2d, 0xaa, 0x98, 0x31, 0xd1, 0x77, 0x35, 0xa8, 0x3b, 0x40,
75  };
76  STATIC_ASSERT(sizeof(xor_table) == sizeof(data));
77 
78  for (unsigned i = 0; i < sizeof(data); i++) {
79  data[i] ^= xor_table[i];
80  }
81 }
82 
83 static uint32_t jmb_crc(const uint8_t (& data)[512])
84 {
85  static const uint32_t crc_table[] = { // Polynomial 0x04c11db7
86  0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
87  0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
88  0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
89  0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
90  0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
91  0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
92  0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
93  0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
94  0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
95  0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
96  0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
97  0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
98  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
99  0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
100  0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
101  0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
102  0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
103  0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
104  0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
105  0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
106  0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
107  0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
108  0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
109  0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
110  0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
111  0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
112  0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
113  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
114  0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
115  0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
116  0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
117  0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
118  };
119  STATIC_ASSERT(sizeof(crc_table) == 256*sizeof(uint32_t));
120 
121  uint32_t crc = 0x52325032;
122  for (unsigned i = 0; i < sizeof(data)/sizeof(uint32_t) - 1; i++) {
123  uint32_t dw = sg_get_unaligned_be32(data + i*sizeof(uint32_t));
124  crc = crc_table[( dw & 0xff) ^ (crc >> 24)] ^ (crc << 8);
125  crc = crc_table[((dw>> 8) & 0xff) ^ (crc >> 24)] ^ (crc << 8);
126  crc = crc_table[((dw>>16) & 0xff) ^ (crc >> 24)] ^ (crc << 8);
127  crc = crc_table[( dw>>24 ) ^ (crc >> 24)] ^ (crc << 8);
128  }
129  return crc;
130 }
131 
132 static inline uint32_t jmb_get_crc(const uint8_t (& data)[512])
133 {
134  return sg_get_unaligned_le32(data + sizeof(data) - 4);
135 }
136 
137 static inline void jmb_put_crc(uint8_t (& data)[512], uint32_t crc)
138 {
139  sg_put_unaligned_le32(crc, data + sizeof(data) - 4);
140 }
141 
142 static inline bool jmb_check_crc(const uint8_t (& data)[512])
143 {
144  return (jmb_get_crc(data) == jmb_crc(data));
145 }
146 
147 static inline void jmb_put_le32(uint8_t (& data)[512], unsigned index, uint32_t val)
148 {
149  jmbassert(index + 4 <= sizeof(data));
150  sg_put_unaligned_le32(val, data + index);
151 }
152 
153 static void jmb_set_wakeup_sector(uint8_t (& data)[512], int id)
154 {
155  uint32_t code = 0, crc = 0;
156  switch (id) {
157  case 0: code = 0x3c75a80b; crc = 0x706d10d9; break;
158  case 1: code = 0x0388e337; crc = 0x6958511e; break;
159  case 2: code = 0x689705f3; crc = 0xfe234b07; break;
160  case 3: code = 0xe00c523a; crc = 0x5be57adb; break;
161  default: jmbassert(false);
162  }
163  jmb_put_le32(data, 0, 0x197b0325); // WAKEUP_CMD
164  jmb_put_le32(data, 4, code);
165  memset(data + 8, 0, 8);
166  for (unsigned i = 16; i < sizeof(data) - 8; i++)
167  data[i] = (uint8_t)i;
168  jmb_put_le32(data, sizeof(data) - 8, 0x10eca1db);
169  jmb_put_crc(data, crc);
170 }
171 
172 static void jmb_set_request_sector(uint8_t (& data)[512], uint8_t version, uint32_t cmd_id,
173  const uint8_t * cmd, unsigned cmdsize)
174 {
175  jmbassert(4 <= cmdsize && cmdsize <= 24);
176  memset(data, 0, sizeof(data));
177 
178  uint32_t scrambled_cmd_code;
179  switch (version) {
180  default:
181  case 0: scrambled_cmd_code = 0x197b0322; break; // JMB39x: various devices
182  case 1: scrambled_cmd_code = 0x197b0393; break; // JMB39x: QNAP TR-004 NAS
183  case 2: scrambled_cmd_code = 0x197b0562; break; // JMS562
184  }
185  jmb_put_le32(data, 0, scrambled_cmd_code);
186 
187  jmb_put_le32(data, 4, cmd_id);
188  memcpy(data + 8, cmd, cmdsize);
190 }
191 
192 static int jmb_get_sector_type(const uint8_t (& data)[512])
193 {
194  if (jmb_check_crc(data))
195  return 1; // Plain (wakeup) sector
196  uint8_t data2[512];
197  memcpy(data2, data, sizeof(data2));
198  jmb_xor(data2);
199  if (jmb_check_crc(data2))
200  return 2; // Obfuscated (request/response) sector
201  return 0;
202 }
203 
204 static void jmb_check_funcs()
205 {
206  uint8_t data[512];
214  jmb_xor(data);
215  jmbassert(jmb_crc(data) == 0x053ed64b);
216  jmb_xor(data);
220  uint8_t cmd[] = {1, 2, 3, 4, 5, 6, 7};
221  jmb_set_request_sector(data, 0, 42, cmd, sizeof(cmd));
222  jmbassert(jmb_get_crc(data) == 0xb1f765d7);
224  jmb_set_request_sector(data, 1, 42, cmd, sizeof(cmd));
225  jmbassert(jmb_get_crc(data) == 0x388b2759);
227  jmb_set_request_sector(data, 2, 42, cmd, sizeof(cmd));
228  jmbassert(jmb_get_crc(data) == 0xde10952b);
230  jmb_xor(data);
232 }
233 
234 /////////////////////////////////////////////////////////////////////////////
235 
236 static bool ata_read_lba8(ata_device * atadev, uint8_t lba8, uint8_t (& data)[512])
237 {
238  ata_cmd_in in;
239  in.in_regs.command = 0x20; // READ SECTORS, 28-bit PIO
240  in.set_data_in(data, 1);
241  in.in_regs.lba_low = lba8;
242  in.in_regs.lba_mid = 0;
243  in.in_regs.lba_high = 0;
244  in.in_regs.device = 0x40; // LBA mode | LBA bits 24-27
245  if (!atadev->ata_pass_through(in))
246  return false;
247  return true;
248 }
249 
250 static bool ata_write_lba8(ata_device * atadev, uint8_t lba8, const uint8_t (& data)[512])
251 {
252  ata_cmd_in in;
253  in.in_regs.command = 0x30; // WRITE SECTORS, 28-bit PIO
254  in.set_data_out(data, 1);
255  in.in_regs.lba_low = lba8;
256  in.in_regs.lba_mid = 0;
257  in.in_regs.lba_high = 0;
258  in.in_regs.device = 0x40; // LBA mode | LBA bits 24-27
259  if (!atadev->ata_pass_through(in))
260  return false;
261  return true;
262 }
263 
264 static int scsi_get_lba_size(scsi_device * scsidev)
265 {
266  scsi_readcap_resp srr; memset(&srr, 0, sizeof(srr));
267  if (!scsiGetSize(scsidev, false /*avoid_rcap16*/, &srr))
268  return -1;
269  return srr.lb_size;
270 }
271 
272 static bool scsi_read_lba8(scsi_device * scsidev, uint8_t lba8, uint8_t (& data)[512])
273 {
274  struct scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr));
275  io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
276  io_hdr.dxfer_len = 512;
277  io_hdr.dxferp = data;
278  uint8_t cdb[] = {0x28 /* READ(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
279  STATIC_ASSERT(sizeof(cdb) == 10);
280  io_hdr.cmnd = cdb;
281  io_hdr.cmnd_len = sizeof(cdb);
282  io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
283 
284  if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_read_lba"))
285  return false;
286  return true;
287 }
288 
289 static bool scsi_write_lba8(scsi_device * scsidev, uint8_t lba8, const uint8_t (& data)[512])
290 {
291  struct scsi_cmnd_io io_hdr; memset(&io_hdr, 0, sizeof(io_hdr));
292  io_hdr.dxfer_dir = DXFER_TO_DEVICE;
293  io_hdr.dxfer_len = 512;
294  io_hdr.dxferp = const_cast<uint8_t *>(data);
295  uint8_t cdb[] = {0x2a /* WRITE(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
296  STATIC_ASSERT(sizeof(cdb) == 10);
297  io_hdr.cmnd = cdb;
298  io_hdr.cmnd_len = sizeof(cdb);
299  io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
300 
301  if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_write_lba"))
302  return false;
303  return true;
304 }
305 
306 /////////////////////////////////////////////////////////////////////////////
307 
308 namespace jmb39x {
309 
311 : public tunnelled_device<
312  /*implements*/ ata_device,
313  /*by tunnelling through a ATA or SCSI*/ smart_device
314 >
315 {
316 public:
317  jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
318  uint8_t version, uint8_t port, uint8_t lba, bool force);
319 
320  virtual ~jmb39x_device();
321 
322  virtual bool open() override;
323 
324  virtual bool close() override;
325 
326  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
327 
328 private:
329  uint8_t m_version;
330  uint8_t m_port;
331  uint8_t m_lba;
332  bool m_force;
333 
334  bool m_blocked;
336  uint32_t m_cmd_id;
337  uint8_t m_orig_data[512];
338 
339  bool raw_read(uint8_t (& data)[512]);
340  bool raw_write(const uint8_t (& data)[512]);
341  bool run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512]);
342  void report_orig_data_lost() const;
343  bool restore_orig_data();
344 };
345 
346 jmb39x_device::jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
347  uint8_t version, uint8_t port, uint8_t lba, bool force)
348 : smart_device(intf, smartdev->get_dev_name(), req_type, req_type),
350  m_version(version), m_port(port), m_lba(lba), m_force(force),
351  m_blocked(false), m_orig_write_back(false), m_cmd_id(0)
352 {
353  set_info().info_name = strprintf("%s [jmb39x_disk_%u]", smartdev->get_info_name(), port);
354  memset(m_orig_data, 0, sizeof(m_orig_data));
355 }
356 
358 {
359  if (m_orig_write_back) try {
361  } catch (...) {
362  // ignore
363  }
364 }
365 
366 bool jmb39x_device::raw_read(uint8_t (& data)[512])
367 {
368  memset(data, 0, sizeof(data));
369  if (get_tunnel_dev()->is_scsi()) {
371  return set_err(EIO, "SCSI READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
372  }
373  else if (get_tunnel_dev()->is_ata()) {
375  return set_err(EIO, "ATA READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
376  }
377  else {
378  jmbassert(false);
379  }
380  return true;
381 }
382 
383 bool jmb39x_device::raw_write(const uint8_t (& data)[512])
384 {
385  if (get_tunnel_dev()->is_scsi()) {
387  return set_err(EIO, "SCSI WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
388  }
389  else if (get_tunnel_dev()->is_ata()) {
391  return set_err(EIO, "ATA WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
392  }
393  else {
394  jmbassert(false);
395  }
396  return true;
397 }
398 
399 bool jmb39x_device::run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512])
400 {
401  // Set up request
402  uint8_t request[512];
403  jmb_set_request_sector(request, m_version, m_cmd_id, cmd, cmdsize);
404 
405  if (ata_debugmode) {
406  pout("JMB39x: Write request sector #%d\n", m_cmd_id);
407  if (ata_debugmode > 1)
408  dStrHex(request, sizeof(request), 0);
409  }
410 
411  // Write obfuscated request
412  jmb_xor(request);
413  if (!raw_write(request)) {
414  m_blocked = true;
415  return false;
416  }
417  jmb_xor(request);
418 
419  // Read obfuscated response
420  memset(response, 0, sizeof(response));
421  if (!raw_read(response)) {
422  m_blocked = true;
423  return false;
424  }
425  jmb_xor(response);
426 
427  if (ata_debugmode) {
428  pout("JMB39x: Read response sector #%d\n", m_cmd_id);
429  if (ata_debugmode > 1)
430  dStrHex(response, sizeof(response), 0);
431  }
432 
433  // Check result
434  if (!memcmp(request, response, sizeof(request))) { // regular I/O?
435  m_blocked = true;
436  return set_err(EIO, "No JMB39x response detected");
437  }
438  if (!jmb_check_crc(response)) {
439  m_blocked = true;
440  jmb_xor(response);
441  return set_err(EIO, "%s", (!jmb_check_crc(response)
442  ? "CRC error in JMB39x response"
443  : "JMB39x response contains a wakeup sector"));
444  }
445  if (memcmp(request, response, 8)) { // code + id identical?
446  m_blocked = true;
447  return set_err(EIO, "Invalid header in JMB39x response");
448  }
449 
450  m_cmd_id++;
451  return true;
452 }
453 
455 {
456  bool zf = !nonempty(m_orig_data, sizeof(m_orig_data));
457  pout("JMB39x: WARNING: Data (%szero filled) at LBA %d lost\n", (zf ? "" : "not "), m_lba);
458  if (!zf) // Dump lost data
459  dStrHex(m_orig_data, sizeof(m_orig_data), 0);
460 }
461 
463 {
464  if (ata_debugmode)
465  pout("JMB39x: Restore original sector (%szero filled)\n",
466  (nonempty(m_orig_data, sizeof(m_orig_data)) ? "not " : ""));
467  if (!raw_write(m_orig_data)) {
469  m_blocked = true;
470  return false;
471  }
472  return true;
473 }
474 
476 {
477  m_orig_write_back = false;
478  if (m_blocked)
479  return set_err(EIO, "Device blocked due to previous errors");
480 
482  return false;
483 
484  // Check SCSI LBA size (assume 512 if ATA)
485  if (get_tunnel_dev()->is_scsi()) {
486  int lba_size = scsi_get_lba_size(get_tunnel_dev()->to_scsi());
487  if (lba_size < 0) {
488  error_info err = get_tunnel_dev()->get_err();
490  return set_err(err.no, "SCSI READ CAPACITY failed: %s", err.msg.c_str());
491  }
492  if (lba_size != 512) {
494  return set_err(EINVAL, "LBA size is %d but must be 512", lba_size);
495  }
496  }
497 
498  // Read original data
499  if (ata_debugmode)
500  pout("JMB39x: Read original data at LBA %d\n", m_lba);
501  if (!raw_read(m_orig_data)) {
502  error_info err = get_err();
504  return set_err(err);
505  }
506 
507  // Check original data
508  if (nonempty(m_orig_data, sizeof(m_orig_data))) {
509  if (ata_debugmode > 1)
510  dStrHex(m_orig_data, sizeof(m_orig_data), 0);
512  if (!m_force) {
514  m_blocked = true;
515  return set_err(EINVAL, "Original sector at LBA %d %s", m_lba,
516  (st == 0 ? "is not zero filled" :
517  st == 1 ? "contains JMB39x wakeup data"
518  : "contains JMB39x protocol data"));
519  }
520  if (st) {
521  // Zero fill to reset protocol state
522  if (ata_debugmode)
523  pout("JMB39x: Zero filling original data\n");
524  memset(m_orig_data, 0, sizeof(m_orig_data));
525  }
526  }
527 
528  // TODO: Defer SIGINT,... until close()
529 
530  // Write 4 wakeup sectors
531  uint8_t dataout[512];
532  for (int id = 0; id < 4; id++) {
533  jmb_set_wakeup_sector(dataout, id);
534  if (ata_debugmode) {
535  pout("JMB39x: Write wakeup sector #%d\n", id+1);
536  if (ata_debugmode > 1)
537  dStrHex(dataout, sizeof(dataout), 0);
538  }
539  if (!raw_write(dataout)) {
540  error_info err = get_err();
541  if (id > 0)
544  m_blocked = true;
545  return set_err(err.no, "Write of JMB39x wakeup sector #%d: %s", id + 1, err.msg.c_str());
546  }
547  }
548  m_orig_write_back = true;
549 
550  // start command sequence
551  m_cmd_id = 1;
552 
553  // Run JMB identify disk command
554  uint8_t b = (m_version != 1 ? 0x02 : 0x01);
555  uint8_t cmd[24]= {
556  0x00,
557  b, b,
558  0xff,
559  m_port,
560  0x00, 0x00, 0x00,
561  m_port,
562  0x00, 0x00, 0x00,
563  0x00, 0x00, 0x00, 0x00,
564  0x00, 0x00, 0x00, 0x00,
565  0x00, 0x00, 0x00, 0x00
566  };
567  uint8_t (& response)[512] = dataout;
568  if (!run_jmb_command(cmd, sizeof(cmd), response)) {
569  error_info err = get_err();
570  close();
571  return set_err(err);
572  }
573 
574  // Check for device model string
575  if (response[16] < ' ') {
576  close();
577  return set_err(ENOENT, "No device connected to JMB39x port %d", m_port);
578  }
579  return true;
580 }
581 
583 {
584  bool ok = true;
585  if (m_orig_write_back) {
586  ok = restore_orig_data();
587  m_orig_write_back = false;
588  }
589 
591  return false;
592  return ok;
593 }
594 
595 // Return: 0=unsupported, 1=supported, 2=supported and has checksum
596 static int is_supported_by_jmb(const ata_in_regs & r)
597 {
598  switch (r.command) {
599  case ATA_IDENTIFY_DEVICE:
600  return 1; // Checksum is optional
601  case ATA_SMART_CMD:
602  switch (r.features) {
605  return 2;
607  switch (r.lba_low) {
608  case 0x00: return 1; // Log directory
609  case 0x01: return 2; // Summary Error log
610  case 0xe0: return 1; // SCT Command/Status
611  }
612  break;
613  }
614  break;
615  }
616  return 0;
617 }
618 
620 {
621  jmbassert(is_open());
622  if (m_blocked)
623  return set_err(EIO, "Device blocked due to previous errors");
624  if (in.direction == ata_cmd_in::no_data) // TODO: add to ata_cmd_is_supported() ?
625  return set_err(ENOSYS, "NO DATA ATA commands not implemented [JMB39x]");
626  if (!ata_cmd_is_supported(in, 0, "JMB39x"))
627  return false;
628  // Block all commands which require full sector data
629  int supported = is_supported_by_jmb(in.in_regs);
630  if (!supported)
631  return set_err(ENOSYS, "ATA command not implemented due to truncated response [JMB39x]");
633 
634  // Run ATA pass-through command
635  uint8_t cmd[24]= {
636  0x00, 0x02, 0x03, 0xff,
637  m_port,
638  0x02, 0x00, 0xe0, 0x00, 0x00,
639  // Registers
640  in.in_regs.features,
641  0x00,
643  0x00,
644  in.in_regs.lba_low,
645  0x00,
646  in.in_regs.lba_mid,
647  0x00,
648  in.in_regs.lba_high,
649  0x00,
650  0xa0, // in.in_regs.device ?
651  0x00,
652  in.in_regs.command,
653  0x00 // status register returned here
654  };
655  uint8_t response[512];
656  if (!run_jmb_command(cmd, sizeof(cmd), response))
657  return false;
658 
659  // Check status register
660  uint8_t status = response[31];
661  if (status == 0x00) {
662  m_blocked = true;
663  return set_err(EIO, "No device connected to JMB39x port %d", m_port);
664  }
665  if ((status & 0xc1) != 0x40 /* !(!BSY && DRDY && !ERR) */)
666  return set_err(EIO, "ATA command failed (status=0x%02x)", status);
667 
668  // Copy data
669  jmbassert(in.size == sizeof(response));
670  memset(in.buffer, 0, in.size);
671  memcpy(in.buffer, response + 32, in.size - 32 - 16);
672 
673  // Prevent checksum warning
674  if (supported > 1)
675  ((uint8_t *)in.buffer)[512-1] -= checksum(in.buffer);
676 
677  return true;
678 }
679 
680 } // namespace jmb39x
681 
683 {
684  jmbassert(smartdev != 0);
685  // Take temporary ownership of 'smartdev' to delete it on error
686  smart_device_auto_ptr smartdev_holder(smartdev);
687  jmb_check_funcs();
688 
689  // Base device must be ATA or SCSI
690  if (!(smartdev->is_ata() || smartdev->is_scsi())) {
691  set_err(EINVAL, "Type '%s+...': Device type '%s' is not ATA or SCSI", type, smartdev->get_req_type());
692  return 0;
693  }
694 
695  int n1 = -1;
696  char prefix[15+1] = "";
697  sscanf(type, "%15[^,],%n", prefix, &n1);
698  uint8_t version;
699  if (!strcmp(prefix, "jmb39x"))
700  version = 0;
701  else if (!strcmp(prefix, "jmb39x-q"))
702  version = 1;
703  else if (!strcmp(prefix, "jms56x"))
704  version = 2;
705  else
706  n1 = -1;
707  if (n1 < 0) {
708  set_err(EINVAL, "Unknown JMicron type '%s'", type);
709  return 0;
710  }
711 
712  // Use default LBA 33, same as JMraidcon.
713  // MBR disk: Zero filled if there is no boot code in boot area.
714  // GPT disk: Zero filled if GPT entries 125-128 are empty.
715  unsigned lba = 33;
716 
717  unsigned port = ~0;
718  bool force = false;
719  const char * args = type + n1;
720  n1 = -1;
721  sscanf(args, "%u%n", &port, &n1);
722  int n2 = -1, len = strlen(args);
723  if (0 < n1 && n1 < len && sscanf(args + n1, ",s%u%n", &lba, &n2) == 1 && n2 > 0)
724  n1 += n2;
725  n2 = -1;
726  if (0 < n1 && n1 < len && (sscanf(args + n1, ",force%n", &n2), n2) > 0) {
727  force = true;
728  n1 += n2;
729  }
730  if (!(n1 == len && port <= 4 && 33 <= lba && lba <= 62)) {
731  set_err(EINVAL, "Option -d %s,N[,sLBA][,force] must have 0 <= N <= 4 [, 33 <= LBA <= 62]", prefix);
732  return 0;
733  }
734 
735  ata_device * jmbdev = new jmb39x::jmb39x_device(this, smartdev, type, version, port, lba, force);
736  // 'smartdev' is now owned by 'jmbdev'
737  smartdev_holder.release();
738  return jmbdev;
739 }
unsigned char checksum(const void *data)
Definition: atacmds.cpp:716
unsigned char ata_debugmode
Definition: atacmds.cpp:33
#define ATA_IDENTIFY_DEVICE
Definition: atacmds.h:53
#define ATA_SMART_READ_VALUES
Definition: atacmds.h:81
#define ATA_SMART_READ_THRESHOLDS
Definition: atacmds.h:82
#define ATA_SMART_READ_LOG_SECTOR
Definition: atacmds.h:86
#define ATA_SMART_CMD
Definition: atacmds.h:56
Smart pointer class for device pointers.
device_type * release()
Return the pointer and release ownership.
ATA device access.
bool ata_cmd_is_supported(const ata_cmd_in &in, unsigned flags, const char *type=0)
Check command input parameters.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)=0
ATA pass through.
bool run_jmb_command(const uint8_t *cmd, unsigned cmdsize, uint8_t(&response)[512])
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out) override
ATA pass through.
void report_orig_data_lost() const
virtual bool close() override
Close device, return false on error.
jmb39x_device(smart_interface *intf, smart_device *smartdev, const char *req_type, uint8_t version, uint8_t port, uint8_t lba, bool force)
virtual bool open() override
Open device, return false on error.
bool raw_read(uint8_t(&data)[512])
bool raw_write(const uint8_t(&data)[512])
SCSI device access.
bool scsi_pass_through_and_check(scsi_cmnd_io *iop, const char *msg="")
Base class for all devices.
Definition: dev_interface.h:33
bool is_scsi() const
Return true if SCSI device.
Definition: dev_interface.h:89
const char * get_info_name() const
Get informal name.
const char * get_req_type() const
Get type requested by user, empty if none.
const char * get_errmsg() const
Get last error message.
const error_info & get_err() const
Get last error info struct.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
ata_device * to_ata()
Downcast to ATA device.
Definition: dev_interface.h:96
bool is_ata() const
Return true if ATA device.
Definition: dev_interface.h:86
device_info & set_info()
R/W access to device info struct.
scsi_device * to_scsi()
Downcast to SCSI device.
The platform interface abstraction.
virtual ata_device * get_jmb39x_device(const char *type, smart_device *smartdev)
Return JMB93x->ATA filter.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
virtual bool is_open() const override
Return true if device is open.
virtual bool close() override
Close device, return false on error.
Implement a device by tunneling through another device.
Definition: dev_tunnelled.h:56
static bool scsi_read_lba8(scsi_device *scsidev, uint8_t lba8, uint8_t(&data)[512])
static void jmb_put_crc(uint8_t(&data)[512], uint32_t crc)
static bool ata_write_lba8(ata_device *atadev, uint8_t lba8, const uint8_t(&data)[512])
const char * dev_jmb39x_raid_cpp_svnid
static uint32_t jmb_get_crc(const uint8_t(&data)[512])
static int scsi_get_lba_size(scsi_device *scsidev)
static bool jmb_check_crc(const uint8_t(&data)[512])
#define jmbassert(expr)
static bool scsi_write_lba8(scsi_device *scsidev, uint8_t lba8, const uint8_t(&data)[512])
static void jmb_put_le32(uint8_t(&data)[512], unsigned index, uint32_t val)
static uint32_t jmb_crc(const uint8_t(&data)[512])
static void jmbassert_failed(int line, const char *expr)
static bool ata_read_lba8(ata_device *atadev, uint8_t lba8, uint8_t(&data)[512])
static void jmb_set_request_sector(uint8_t(&data)[512], uint8_t version, uint32_t cmd_id, const uint8_t *cmd, unsigned cmdsize)
static void jmb_check_funcs()
static void jmb_set_wakeup_sector(uint8_t(&data)[512], int id)
static int jmb_get_sector_type(const uint8_t(&data)[512])
static void jmb_xor(uint8_t(&data)[512])
u8 cmd
Definition: megaraid.h:1
u8 cdb[16]
Definition: megaraid.h:21
u8 b[12]
Definition: megaraid.h:17
ptr_t data
Definition: megaraid.h:15
static int is_supported_by_jmb(const ata_in_regs &r)
uint64_t scsiGetSize(scsi_device *device, bool avoid_rcap16, struct scsi_readcap_resp *srrp)
Definition: scsicmds.cpp:1341
void dStrHex(const uint8_t *up, int len, int no_ascii)
Definition: scsicmds.cpp:78
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:97
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:352
#define DXFER_TO_DEVICE
Definition: scsicmds.h:98
static void sg_put_unaligned_le32(uint32_t val, void *p)
Definition: sg_unaligned.h:315
static uint32_t sg_get_unaligned_le32(const void *p)
Definition: sg_unaligned.h:297
static uint32_t sg_get_unaligned_be32(const void *p)
Definition: sg_unaligned.h:261
void pout(const char *fmt,...)
Definition: smartd.cpp:1308
#define STATIC_ASSERT(x)
Definition: static_assert.h:24
ATA pass through input parameters.
enum ata_cmd_in::@29 direction
I/O direction.
void set_data_out(const void *buf, unsigned nsectors)
Prepare for 28-bit DATA OUT command.
void * buffer
Pointer to data buffer.
ata_in_regs_48bit in_regs
Input registers.
unsigned size
Size of buffer.
void set_data_in(void *buf, unsigned nsectors)
Prepare for 28-bit DATA IN command.
ATA pass through output parameters.
ATA Input registers (for 28-bit commands)
ata_register device
ata_register lba_high
ata_register sector_count
ata_register lba_low
ata_register features
ata_register command
ata_register lba_mid
uint8_t * dxferp
Definition: scsicmds.h:106
int dxfer_dir
Definition: scsicmds.h:104
size_t cmnd_len
Definition: scsicmds.h:103
size_t dxfer_len
Definition: scsicmds.h:107
uint8_t * cmnd
Definition: scsicmds.h:102
unsigned timeout
Definition: scsicmds.h:111
uint32_t lb_size
Definition: scsicmds.h:159
std::string info_name
Informal name.
Definition: dev_interface.h:46
Error (number,message) pair.
Definition: dev_interface.h:52
std::string msg
Error message.
Definition: dev_interface.h:61
int no
Error number.
Definition: dev_interface.h:60
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:772
bool nonempty(const void *data, int size)
Definition: utility.cpp:655