smartmontools SVN Rev 5407
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-22 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
28const char * dev_jmb39x_raid_cpp_svnid = "$Id: dev_jmb39x_raid.cpp 5366 2022-04-30 15:26:22Z chrfranke $";
29
30static 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
40static 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
83static 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
132static inline uint32_t jmb_get_crc(const uint8_t (& data)[512])
133{
134 return sg_get_unaligned_le32(data + sizeof(data) - 4);
135}
136
137static 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
142static inline bool jmb_check_crc(const uint8_t (& data)[512])
143{
144 return (jmb_get_crc(data) == jmb_crc(data));
145}
146
147static 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
153static 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
172static 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
192static 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
204static 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
236static 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
250static 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
264static 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
272static bool scsi_read_lba8(scsi_device * scsidev, uint8_t lba8, uint8_t (& data)[512])
273{
274 struct scsi_cmnd_io io_hdr = {};
275
277 io_hdr.dxfer_len = 512;
278 io_hdr.dxferp = data;
279 uint8_t cdb[] = {0x28 /* READ(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
280 STATIC_ASSERT(sizeof(cdb) == 10);
281 io_hdr.cmnd = cdb;
282 io_hdr.cmnd_len = sizeof(cdb);
284
285 if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_read_lba"))
286 return false;
287 return true;
288}
289
290static bool scsi_write_lba8(scsi_device * scsidev, uint8_t lba8, const uint8_t (& data)[512])
291{
292 struct scsi_cmnd_io io_hdr = {};
293
294 io_hdr.dxfer_dir = DXFER_TO_DEVICE;
295 io_hdr.dxfer_len = 512;
296 io_hdr.dxferp = const_cast<uint8_t *>(data);
297 uint8_t cdb[] = {0x2a /* WRITE(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
298 STATIC_ASSERT(sizeof(cdb) == 10);
299 io_hdr.cmnd = cdb;
300 io_hdr.cmnd_len = sizeof(cdb);
302
303 if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_write_lba"))
304 return false;
305 return true;
306}
307
308/////////////////////////////////////////////////////////////////////////////
309
310namespace jmb39x {
311
313: public tunnelled_device<
314 /*implements*/ ata_device,
315 /*by tunnelling through a ATA or SCSI*/ smart_device
316>
317{
318public:
319 jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
320 uint8_t version, uint8_t port, uint8_t lba, bool force);
321
322 virtual ~jmb39x_device();
323
324 virtual bool open() override;
325
326 virtual bool close() override;
327
328 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
329
330private:
331 uint8_t m_version;
332 uint8_t m_port;
333 uint8_t m_lba;
335
338 uint32_t m_cmd_id;
339 uint8_t m_orig_data[512];
340
341 bool raw_read(uint8_t (& data)[512]);
342 bool raw_write(const uint8_t (& data)[512]);
343 bool run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512]);
344 void report_orig_data_lost() const;
345 bool restore_orig_data();
346};
347
348jmb39x_device::jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
349 uint8_t version, uint8_t port, uint8_t lba, bool force)
350: smart_device(intf, smartdev->get_dev_name(), req_type, req_type),
352 m_version(version), m_port(port), m_lba(lba), m_force(force),
353 m_blocked(false), m_orig_write_back(false), m_cmd_id(0)
354{
355 set_info().info_name = strprintf("%s [jmb39x_disk_%u]", smartdev->get_info_name(), port);
356 memset(m_orig_data, 0, sizeof(m_orig_data));
357}
358
360{
361 if (m_orig_write_back) try {
363 } catch (...) {
364 // ignore
365 }
366}
367
368bool jmb39x_device::raw_read(uint8_t (& data)[512])
369{
370 memset(data, 0, sizeof(data));
371 if (get_tunnel_dev()->is_scsi()) {
373 return set_err(EIO, "SCSI READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
374 }
375 else if (get_tunnel_dev()->is_ata()) {
377 return set_err(EIO, "ATA READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
378 }
379 else {
380 jmbassert(false);
381 }
382 return true;
383}
384
385bool jmb39x_device::raw_write(const uint8_t (& data)[512])
386{
387 if (get_tunnel_dev()->is_scsi()) {
389 return set_err(EIO, "SCSI WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
390 }
391 else if (get_tunnel_dev()->is_ata()) {
393 return set_err(EIO, "ATA WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
394 }
395 else {
396 jmbassert(false);
397 }
398 return true;
399}
400
401bool jmb39x_device::run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512])
402{
403 // Set up request
404 uint8_t request[512];
405 jmb_set_request_sector(request, m_version, m_cmd_id, cmd, cmdsize);
406
407 if (ata_debugmode) {
408 pout("JMB39x: Write request sector #%d\n", m_cmd_id);
409 if (ata_debugmode > 1)
410 dStrHex(request, sizeof(request), 0);
411 }
412
413 // Write obfuscated request
414 jmb_xor(request);
415 if (!raw_write(request)) {
416 m_blocked = true;
417 return false;
418 }
419 jmb_xor(request);
420
421 // Read obfuscated response
422 memset(response, 0, sizeof(response));
423 if (!raw_read(response)) {
424 m_blocked = true;
425 return false;
426 }
427 jmb_xor(response);
428
429 if (ata_debugmode) {
430 pout("JMB39x: Read response sector #%d\n", m_cmd_id);
431 if (ata_debugmode > 1)
432 dStrHex(response, sizeof(response), 0);
433 }
434
435 // Check result
436 if (!memcmp(request, response, sizeof(request))) { // regular I/O?
437 m_blocked = true;
438 return set_err(EIO, "No JMB39x response detected");
439 }
440 if (!jmb_check_crc(response)) {
441 m_blocked = true;
442 jmb_xor(response);
443 return set_err(EIO, "%s", (!jmb_check_crc(response)
444 ? "CRC error in JMB39x response"
445 : "JMB39x response contains a wakeup sector"));
446 }
447 if (memcmp(request, response, 8)) { // code + id identical?
448 m_blocked = true;
449 return set_err(EIO, "Invalid header in JMB39x response");
450 }
451
452 m_cmd_id++;
453 return true;
454}
455
457{
458 bool zf = !nonempty(m_orig_data, sizeof(m_orig_data));
459 pout("JMB39x: WARNING: Data (%szero filled) at LBA %d lost\n", (zf ? "" : "not "), m_lba);
460 if (!zf) // Dump lost data
461 dStrHex(m_orig_data, sizeof(m_orig_data), 0);
462}
463
465{
466 if (ata_debugmode)
467 pout("JMB39x: Restore original sector (%szero filled)\n",
468 (nonempty(m_orig_data, sizeof(m_orig_data)) ? "not " : ""));
469 if (!raw_write(m_orig_data)) {
471 m_blocked = true;
472 return false;
473 }
474 return true;
475}
476
478{
479 m_orig_write_back = false;
480 if (m_blocked)
481 return set_err(EIO, "Device blocked due to previous errors");
482
484 return false;
485
486 // Check SCSI LBA size (assume 512 if ATA)
487 if (get_tunnel_dev()->is_scsi()) {
488 int lba_size = scsi_get_lba_size(get_tunnel_dev()->to_scsi());
489 if (lba_size < 0) {
492 return set_err(err.no, "SCSI READ CAPACITY failed: %s", err.msg.c_str());
493 }
494 if (lba_size != 512) {
496 return set_err(EINVAL, "LBA size is %d but must be 512", lba_size);
497 }
498 }
499
500 // Read original data
501 if (ata_debugmode)
502 pout("JMB39x: Read original data at LBA %d\n", m_lba);
503 if (!raw_read(m_orig_data)) {
504 error_info err = get_err();
506 return set_err(err);
507 }
508
509 // Check original data
510 if (nonempty(m_orig_data, sizeof(m_orig_data))) {
511 if (ata_debugmode > 1)
512 dStrHex(m_orig_data, sizeof(m_orig_data), 0);
514 if (!m_force) {
516 m_blocked = true;
517 return set_err(EINVAL, "Original sector at LBA %d %s", m_lba,
518 (st == 0 ? "is not zero filled" :
519 st == 1 ? "contains JMB39x wakeup data"
520 : "contains JMB39x protocol data"));
521 }
522 if (st) {
523 // Zero fill to reset protocol state
524 if (ata_debugmode)
525 pout("JMB39x: Zero filling original data\n");
526 memset(m_orig_data, 0, sizeof(m_orig_data));
527 }
528 }
529
530 // TODO: Defer SIGINT,... until close()
531
532 // Write 4 wakeup sectors
533 uint8_t dataout[512];
534 for (int id = 0; id < 4; id++) {
535 jmb_set_wakeup_sector(dataout, id);
536 if (ata_debugmode) {
537 pout("JMB39x: Write wakeup sector #%d\n", id+1);
538 if (ata_debugmode > 1)
539 dStrHex(dataout, sizeof(dataout), 0);
540 }
541 if (!raw_write(dataout)) {
542 error_info err = get_err();
543 if (id > 0)
546 m_blocked = true;
547 return set_err(err.no, "Write of JMB39x wakeup sector #%d: %s", id + 1, err.msg.c_str());
548 }
549 }
550 m_orig_write_back = true;
551
552 // start command sequence
553 m_cmd_id = 1;
554
555 // Run JMB identify disk command
556 uint8_t b = (m_version != 1 ? 0x02 : 0x01);
557 uint8_t cmd[24]= {
558 0x00,
559 b, b,
560 0xff,
561 m_port,
562 0x00, 0x00, 0x00,
563 m_port,
564 0x00, 0x00, 0x00,
565 0x00, 0x00, 0x00, 0x00,
566 0x00, 0x00, 0x00, 0x00,
567 0x00, 0x00, 0x00, 0x00
568 };
569 uint8_t (& response)[512] = dataout;
570 if (!run_jmb_command(cmd, sizeof(cmd), response)) {
571 error_info err = get_err();
572 close();
573 return set_err(err);
574 }
575
576 // Check for device model string
577 if (response[16] < ' ') {
578 close();
579 return set_err(ENOENT, "No device connected to JMB39x port %d", m_port);
580 }
581 return true;
582}
583
585{
586 bool ok = true;
587 if (m_orig_write_back) {
588 ok = restore_orig_data();
589 m_orig_write_back = false;
590 }
591
593 return false;
594 return ok;
595}
596
597// Return: 0=unsupported, 1=supported, 2=supported and has checksum
598static int is_supported_by_jmb(const ata_in_regs & r)
599{
600 switch (r.command) {
602 return 1; // Checksum is optional
603 case ATA_SMART_CMD:
604 switch (r.features) {
607 return 2;
609 switch (r.lba_low) {
610 case 0x00: return 1; // Log directory
611 case 0x01: return 2; // Summary Error log
612 case 0xe0: return 1; // SCT Command/Status
613 }
614 break;
615 }
616 break;
617 }
618 return 0;
619}
620
622{
624 if (m_blocked)
625 return set_err(EIO, "Device blocked due to previous errors");
626 if (in.direction == ata_cmd_in::no_data) // TODO: add to ata_cmd_is_supported() ?
627 return set_err(ENOSYS, "NO DATA ATA commands not implemented [JMB39x]");
628 if (!ata_cmd_is_supported(in, 0, "JMB39x"))
629 return false;
630 // Block all commands which require full sector data
631 int supported = is_supported_by_jmb(in.in_regs);
632 if (!supported)
633 return set_err(ENOSYS, "ATA command not implemented due to truncated response [JMB39x]");
635
636 // Run ATA pass-through command
637 uint8_t cmd[24]= {
638 0x00, 0x02, 0x03, 0xff,
639 m_port,
640 0x02, 0x00, 0xe0, 0x00, 0x00,
641 // Registers
642 in.in_regs.features,
643 0x00,
645 0x00,
646 in.in_regs.lba_low,
647 0x00,
648 in.in_regs.lba_mid,
649 0x00,
650 in.in_regs.lba_high,
651 0x00,
652 0xa0, // in.in_regs.device ?
653 0x00,
654 in.in_regs.command,
655 0x00 // status register returned here
656 };
657 uint8_t response[512];
658 if (!run_jmb_command(cmd, sizeof(cmd), response))
659 return false;
660
661 // Check status register
662 uint8_t status = response[31];
663 if (status == 0x00) {
664 m_blocked = true;
665 return set_err(EIO, "No device connected to JMB39x port %d", m_port);
666 }
667 if ((status & 0xc1) != 0x40 /* !(!BSY && DRDY && !ERR) */)
668 return set_err(EIO, "ATA command failed (status=0x%02x)", status);
669
670 // Copy data
671 jmbassert(in.size == sizeof(response));
672 memset(in.buffer, 0, in.size);
673 memcpy(in.buffer, response + 32, in.size - 32 - 16);
674
675 // Prevent checksum warning
676 if (supported > 1)
677 ((uint8_t *)in.buffer)[512-1] -= checksum(in.buffer);
678
679 return true;
680}
681
682} // namespace jmb39x
683
685{
686 jmbassert(smartdev != 0);
687 // Take temporary ownership of 'smartdev' to delete it on error
688 smart_device_auto_ptr smartdev_holder(smartdev);
690
691 // Base device must be ATA or SCSI
692 if (!(smartdev->is_ata() || smartdev->is_scsi())) {
693 set_err(EINVAL, "Type '%s+...': Device type '%s' is not ATA or SCSI", type, smartdev->get_req_type());
694 return 0;
695 }
696
697 int n1 = -1;
698 char prefix[15+1] = "";
699 sscanf(type, "%15[^,],%n", prefix, &n1);
700 uint8_t version;
701 if (!strcmp(prefix, "jmb39x"))
702 version = 0;
703 else if (!strcmp(prefix, "jmb39x-q"))
704 version = 1;
705 else if (!strcmp(prefix, "jms56x"))
706 version = 2;
707 else
708 n1 = -1;
709 if (n1 < 0) {
710 set_err(EINVAL, "Unknown JMicron type '%s'", type);
711 return 0;
712 }
713
714 // Use default LBA 33, same as JMraidcon.
715 // MBR disk: Zero filled if there is no boot code in boot area.
716 // GPT disk: Zero filled if GPT entries 125-128 are empty.
717 unsigned lba = 33;
718
719 unsigned port = ~0;
720 bool force = false;
721 const char * args = type + n1;
722 n1 = -1;
723 sscanf(args, "%u%n", &port, &n1);
724 int n2 = -1, len = strlen(args);
725 if (0 < n1 && n1 < len && sscanf(args + n1, ",s%u%n", &lba, &n2) == 1 && n2 > 0)
726 n1 += n2;
727 n2 = -1;
728 if (0 < n1 && n1 < len && (sscanf(args + n1, ",force%n", &n2), n2) > 0) {
729 force = true;
730 n1 += n2;
731 }
732 if (!(n1 == len && port <= 4 && 1 <= lba && lba <= 255)) {
733 set_err(EINVAL, "Option -d %s,N[,sLBA][,force] must have 0 <= N <= 4 [, 1 <= LBA <= 255]", prefix);
734 return 0;
735 }
736
737 ata_device * jmbdev = new jmb39x::jmb39x_device(this, smartdev, type, version, port, lba, force);
738 // 'smartdev' is now owned by 'jmbdev'
739 smartdev_holder.release();
740 return jmbdev;
741}
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 error_info & get_err() const
Get last error info struct.
const char * get_req_type() const
Get type requested by user, empty if none.
const char * get_errmsg() const
Get last error message.
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
const char * get_info_name() const
Get informal name.
scsi_device * to_scsi()
Downcast to SCSI device.
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.
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:1441
void dStrHex(const uint8_t *up, int len, int no_ascii)
Definition: scsicmds.cpp:78
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:106
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:372
#define DXFER_TO_DEVICE
Definition: scsicmds.h:107
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:1326
#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:118
int dxfer_dir
Definition: scsicmds.h:116
size_t cmnd_len
Definition: scsicmds.h:115
size_t dxfer_len
Definition: scsicmds.h:119
uint8_t * cmnd
Definition: scsicmds.h:114
unsigned timeout
Definition: scsicmds.h:123
uint32_t lb_size
Definition: scsicmds.h:171
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:780
bool nonempty(const void *data, int size)
Definition: utility.cpp:663