smartmontools SVN Rev 5645
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 5644 2024-12-10 14:39: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: // JMB39x: QNAP TR-004 NAS
183 case 3: // JMB39x: QNAP TR-002 NAS
184 scrambled_cmd_code = 0x197b0393; break;
185 case 2: scrambled_cmd_code = 0x197b0562; break; // JMS562
186 }
187 jmb_put_le32(data, 0, scrambled_cmd_code);
188
189 jmb_put_le32(data, 4, cmd_id);
190 memcpy(data + 8, cmd, cmdsize);
192}
193
194static int jmb_get_sector_type(const uint8_t (& data)[512])
195{
196 if (jmb_check_crc(data))
197 return 1; // Plain (wakeup) sector
198 uint8_t data2[512];
199 memcpy(data2, data, sizeof(data2));
200 jmb_xor(data2);
201 if (jmb_check_crc(data2))
202 return 2; // Obfuscated (request/response) sector
203 return 0;
204}
205
206static void jmb_check_funcs()
207{
208 uint8_t data[512];
216 jmb_xor(data);
217 jmbassert(jmb_crc(data) == 0x053ed64b);
218 jmb_xor(data);
222 uint8_t cmd[] = {1, 2, 3, 4, 5, 6, 7};
223 jmb_set_request_sector(data, 0, 42, cmd, sizeof(cmd));
224 jmbassert(jmb_get_crc(data) == 0xb1f765d7);
226 jmb_set_request_sector(data, 1, 42, cmd, sizeof(cmd));
227 jmbassert(jmb_get_crc(data) == 0x388b2759);
229 jmb_set_request_sector(data, 2, 42, cmd, sizeof(cmd));
230 jmbassert(jmb_get_crc(data) == 0xde10952b);
232 jmb_xor(data);
234}
235
236/////////////////////////////////////////////////////////////////////////////
237
238static bool ata_read_lba8(ata_device * atadev, uint8_t lba8, uint8_t (& data)[512])
239{
240 ata_cmd_in in;
241 in.in_regs.command = 0x20; // READ SECTORS, 28-bit PIO
242 in.set_data_in(data, 1);
243 in.in_regs.lba_low = lba8;
244 in.in_regs.lba_mid = 0;
245 in.in_regs.lba_high = 0;
246 in.in_regs.device = 0x40; // LBA mode | LBA bits 24-27
247 if (!atadev->ata_pass_through(in))
248 return false;
249 return true;
250}
251
252static bool ata_write_lba8(ata_device * atadev, uint8_t lba8, const uint8_t (& data)[512])
253{
254 ata_cmd_in in;
255 in.in_regs.command = 0x30; // WRITE SECTORS, 28-bit PIO
256 in.set_data_out(data, 1);
257 in.in_regs.lba_low = lba8;
258 in.in_regs.lba_mid = 0;
259 in.in_regs.lba_high = 0;
260 in.in_regs.device = 0x40; // LBA mode | LBA bits 24-27
261 if (!atadev->ata_pass_through(in))
262 return false;
263 return true;
264}
265
266static int scsi_get_lba_size(scsi_device * scsidev)
267{
268 scsi_readcap_resp srr; memset(&srr, 0, sizeof(srr));
269 if (!scsiGetSize(scsidev, false /*avoid_rcap16*/, &srr))
270 return -1;
271 return srr.lb_size;
272}
273
274static bool scsi_read_lba8(scsi_device * scsidev, uint8_t lba8, uint8_t (& data)[512])
275{
276 struct scsi_cmnd_io io_hdr = {};
277
279 io_hdr.dxfer_len = 512;
280 io_hdr.dxferp = data;
281 uint8_t cdb[] = {0x28 /* READ(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
282 STATIC_ASSERT(sizeof(cdb) == 10);
283 io_hdr.cmnd = cdb;
284 io_hdr.cmnd_len = sizeof(cdb);
286
287 if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_read_lba"))
288 return false;
289 return true;
290}
291
292static bool scsi_write_lba8(scsi_device * scsidev, uint8_t lba8, const uint8_t (& data)[512])
293{
294 struct scsi_cmnd_io io_hdr = {};
295
296 io_hdr.dxfer_dir = DXFER_TO_DEVICE;
297 io_hdr.dxfer_len = 512;
298 io_hdr.dxferp = const_cast<uint8_t *>(data);
299 uint8_t cdb[] = {0x2a /* WRITE(10) */, 0x00, 0x00, 0x00, 0x00, lba8, 0x00, 0x00, 0x01, 0x00};
300 STATIC_ASSERT(sizeof(cdb) == 10);
301 io_hdr.cmnd = cdb;
302 io_hdr.cmnd_len = sizeof(cdb);
304
305 if (!scsidev->scsi_pass_through_and_check(&io_hdr, "scsi_write_lba"))
306 return false;
307 return true;
308}
309
310/////////////////////////////////////////////////////////////////////////////
311
312namespace jmb39x {
313
315: public tunnelled_device<
316 /*implements*/ ata_device,
317 /*by tunnelling through a ATA or SCSI*/ smart_device
318>
319{
320public:
321 jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
322 uint8_t version, uint8_t port, uint8_t lba, bool force);
323
324 virtual ~jmb39x_device();
325
326 virtual bool open() override;
327
328 virtual bool close() override;
329
330 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
331
332private:
333 uint8_t m_version;
334 uint8_t m_port;
335 uint8_t m_lba;
337
340 uint32_t m_cmd_id;
341 uint8_t m_orig_data[512];
342
343 bool raw_read(uint8_t (& data)[512]);
344 bool raw_write(const uint8_t (& data)[512]);
345 bool run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512]);
346 void report_orig_data_lost() const;
347 bool restore_orig_data();
348};
349
350jmb39x_device::jmb39x_device(smart_interface * intf, smart_device * smartdev, const char * req_type,
351 uint8_t version, uint8_t port, uint8_t lba, bool force)
352: smart_device(intf, smartdev->get_dev_name(), req_type, req_type),
354 m_version(version), m_port(port), m_lba(lba), m_force(force),
355 m_blocked(false), m_orig_write_back(false), m_cmd_id(0)
356{
357 set_info().info_name = strprintf("%s [jmb39x_disk_%u]", smartdev->get_info_name(), port);
358 memset(m_orig_data, 0, sizeof(m_orig_data));
359}
360
362{
363 if (m_orig_write_back) try {
365 } catch (...) {
366 // ignore
367 }
368}
369
370bool jmb39x_device::raw_read(uint8_t (& data)[512])
371{
372 memset(data, 0, sizeof(data));
373 if (get_tunnel_dev()->is_scsi()) {
375 return set_err(EIO, "SCSI READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
376 }
377 else if (get_tunnel_dev()->is_ata()) {
379 return set_err(EIO, "ATA READ LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
380 }
381 else {
382 jmbassert(false);
383 }
384 return true;
385}
386
387bool jmb39x_device::raw_write(const uint8_t (& data)[512])
388{
389 if (get_tunnel_dev()->is_scsi()) {
391 return set_err(EIO, "SCSI WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
392 }
393 else if (get_tunnel_dev()->is_ata()) {
395 return set_err(EIO, "ATA WRITE LBA %d failed: %s", m_lba, get_tunnel_dev()->get_errmsg());
396 }
397 else {
398 jmbassert(false);
399 }
400 return true;
401}
402
403bool jmb39x_device::run_jmb_command(const uint8_t * cmd, unsigned cmdsize, uint8_t (& response)[512])
404{
405 // Set up request
406 uint8_t request[512];
407 jmb_set_request_sector(request, m_version, m_cmd_id, cmd, cmdsize);
408
409 if (ata_debugmode) {
410 pout("JMB39x: Write request sector #%d\n", m_cmd_id);
411 if (ata_debugmode > 1)
412 dStrHex(request, sizeof(request), 0);
413 }
414
415 // Write obfuscated request
416 jmb_xor(request);
417 if (!raw_write(request)) {
418 m_blocked = true;
419 return false;
420 }
421 jmb_xor(request);
422
423 // Read obfuscated response
424 memset(response, 0, sizeof(response));
425 if (!raw_read(response)) {
426 m_blocked = true;
427 return false;
428 }
429 jmb_xor(response);
430
431 if (ata_debugmode) {
432 pout("JMB39x: Read response sector #%d\n", m_cmd_id);
433 if (ata_debugmode > 1)
434 dStrHex(response, sizeof(response), 0);
435 }
436
437 // Check result
438 if (!memcmp(request, response, sizeof(request))) { // regular I/O?
439 m_blocked = true;
440 return set_err(EIO, "No JMB39x response detected");
441 }
442 if (!jmb_check_crc(response)) {
443 m_blocked = true;
444 jmb_xor(response);
445 return set_err(EIO, "%s", (!jmb_check_crc(response)
446 ? "CRC error in JMB39x response"
447 : "JMB39x response contains a wakeup sector"));
448 }
449 if (memcmp(request, response, 8)) { // code + id identical?
450 m_blocked = true;
451 return set_err(EIO, "Invalid header in JMB39x response");
452 }
453
454 m_cmd_id++;
455 return true;
456}
457
459{
460 bool zf = !nonempty(m_orig_data, sizeof(m_orig_data));
461 pout("JMB39x: WARNING: Data (%szero filled) at LBA %d lost\n", (zf ? "" : "not "), m_lba);
462 if (!zf) // Dump lost data
463 dStrHex(m_orig_data, sizeof(m_orig_data), 0);
464}
465
467{
468 if (ata_debugmode)
469 pout("JMB39x: Restore original sector (%szero filled)\n",
470 (nonempty(m_orig_data, sizeof(m_orig_data)) ? "not " : ""));
471 if (!raw_write(m_orig_data)) {
473 m_blocked = true;
474 return false;
475 }
476 return true;
477}
478
480{
481 m_orig_write_back = false;
482 if (m_blocked)
483 return set_err(EIO, "Device blocked due to previous errors");
484
486 return false;
487
488 // Check SCSI LBA size (assume 512 if ATA)
489 if (get_tunnel_dev()->is_scsi()) {
490 int lba_size = scsi_get_lba_size(get_tunnel_dev()->to_scsi());
491 if (lba_size < 0) {
494 return set_err(err.no, "SCSI READ CAPACITY failed: %s", err.msg.c_str());
495 }
496 if (lba_size != 512) {
498 return set_err(EINVAL, "LBA size is %d but must be 512", lba_size);
499 }
500 }
501
502 // Read original data
503 if (ata_debugmode)
504 pout("JMB39x: Read original data at LBA %d\n", m_lba);
505 if (!raw_read(m_orig_data)) {
506 error_info err = get_err();
508 return set_err(err);
509 }
510
511 // Check original data
512 if (nonempty(m_orig_data, sizeof(m_orig_data))) {
513 if (ata_debugmode > 1)
514 dStrHex(m_orig_data, sizeof(m_orig_data), 0);
516 if (!m_force) {
518 m_blocked = true;
519 return set_err(EINVAL, "Original sector at LBA %d %s", m_lba,
520 (st == 0 ? "is not zero filled" :
521 st == 1 ? "contains JMB39x wakeup data"
522 : "contains JMB39x protocol data"));
523 }
524 if (st) {
525 // Zero fill to reset protocol state
526 if (ata_debugmode)
527 pout("JMB39x: Zero filling original data\n");
528 memset(m_orig_data, 0, sizeof(m_orig_data));
529 }
530 }
531
532 // TODO: Defer SIGINT,... until close()
533
534 // Write 4 wakeup sectors
535 uint8_t dataout[512];
536 for (int id = 0; id < 4; id++) {
537 jmb_set_wakeup_sector(dataout, id);
538 if (ata_debugmode) {
539 pout("JMB39x: Write wakeup sector #%d\n", id+1);
540 if (ata_debugmode > 1)
541 dStrHex(dataout, sizeof(dataout), 0);
542 }
543 if (!raw_write(dataout)) {
544 error_info err = get_err();
545 if (id > 0)
548 m_blocked = true;
549 return set_err(err.no, "Write of JMB39x wakeup sector #%d: %s", id + 1, err.msg.c_str());
550 }
551 }
552 m_orig_write_back = true;
553
554 // start command sequence
555 m_cmd_id = 1;
556
557 // Run JMB identify disk command
558 uint8_t b = (m_version != 1 ? 0x02 : 0x01);
559 uint8_t cmd[24]= {
560 0x00,
561 b, b,
562 0xff,
563 m_port,
564 0x00, 0x00, 0x00,
565 m_port,
566 0x00, 0x00, 0x00,
567 0x00, 0x00, 0x00, 0x00,
568 0x00, 0x00, 0x00, 0x00,
569 0x00, 0x00, 0x00, 0x00
570 };
571 uint8_t (& response)[512] = dataout;
572 if (!run_jmb_command(cmd, sizeof(cmd), response)) {
573 error_info err = get_err();
574 close();
575 return set_err(err);
576 }
577
578 // Check for device model string
579 if (response[16] < ' ') {
580 close();
581 return set_err(ENOENT, "No device connected to JMB39x port %d", m_port);
582 }
583 return true;
584}
585
587{
588 bool ok = true;
589 if (m_orig_write_back) {
590 ok = restore_orig_data();
591 m_orig_write_back = false;
592 }
593
595 return false;
596 return ok;
597}
598
599// Return: 0=unsupported, 1=supported, 2=supported and has checksum
600static int is_supported_by_jmb(const ata_in_regs & r)
601{
602 switch (r.command) {
604 return 1; // Checksum is optional
605 case ATA_SMART_CMD:
606 switch (r.features) {
609 return 2;
611 switch (r.lba_low) {
612 case 0x00: return 1; // Log directory
613 case 0x01: return 2; // Summary Error log
614 case 0xe0: return 1; // SCT Command/Status
615 }
616 break;
617 }
618 break;
619 }
620 return 0;
621}
622
624{
626 if (m_blocked)
627 return set_err(EIO, "Device blocked due to previous errors");
628 if (in.direction == ata_cmd_in::no_data) // TODO: add to ata_cmd_is_supported() ?
629 return set_err(ENOSYS, "NO DATA ATA commands not implemented [JMB39x]");
630 if (!ata_cmd_is_supported(in, 0, "JMB39x"))
631 return false;
632 // Block all commands which require full sector data
633 int supported = is_supported_by_jmb(in.in_regs);
634 if (!supported)
635 return set_err(ENOSYS, "ATA command not implemented due to truncated response [JMB39x]");
637
638 // Run ATA pass-through command
639 uint8_t cmd[24]= {
640 0x00, 0x02, 0x03, 0xff,
641 m_port,
642 0x02, 0x00, 0xe0, 0x00, 0x00,
643 // Registers
644 in.in_regs.features,
645 0x00,
647 0x00,
648 in.in_regs.lba_low,
649 0x00,
650 in.in_regs.lba_mid,
651 0x00,
652 in.in_regs.lba_high,
653 0x00,
654 0xa0, // in.in_regs.device ?
655 0x00,
656 in.in_regs.command,
657 0x00 // status register returned here
658 };
659 uint8_t response[512];
660 if (!run_jmb_command(cmd, sizeof(cmd), response))
661 return false;
662
663 // Check status register
664 uint8_t status = response[31];
665 if (status == 0x00) {
666 m_blocked = true;
667 return set_err(EIO, "No device connected to JMB39x port %d", m_port);
668 }
669 if ((status & 0xc1) != 0x40 /* !(!BSY && DRDY && !ERR) */)
670 return set_err(EIO, "ATA command failed (status=0x%02x)", status);
671
672 // Copy data
673 jmbassert(in.size == sizeof(response));
674 memset(in.buffer, 0, in.size);
675 memcpy(in.buffer, response + 32, in.size - 32 - 16);
676
677 // Prevent checksum warning
678 if (supported > 1)
679 ((uint8_t *)in.buffer)[512-1] -= checksum(in.buffer);
680
681 return true;
682}
683
684} // namespace jmb39x
685
687{
688 jmbassert(smartdev != 0);
689 // Take temporary ownership of 'smartdev' to delete it on error
690 smart_device_auto_ptr smartdev_holder(smartdev);
692
693 // Base device must be ATA or SCSI
694 if (!(smartdev->is_ata() || smartdev->is_scsi())) {
695 set_err(EINVAL, "Type '%s+...': Device type '%s' is not ATA or SCSI", type, smartdev->get_req_type());
696 return 0;
697 }
698
699 int n1 = -1;
700 char prefix[15+1] = "";
701 sscanf(type, "%15[^,],%n", prefix, &n1);
702 uint8_t version;
703 if (!strcmp(prefix, "jmb39x"))
704 version = 0;
705 else if (!strcmp(prefix, "jmb39x-q"))
706 version = 1;
707 else if (!strcmp(prefix, "jms56x"))
708 version = 2;
709 else if (!strcmp(prefix, "jmb39x-q2"))
710 version = 3;
711 else
712 n1 = -1;
713 if (n1 < 0) {
714 set_err(EINVAL, "Unknown JMicron type '%s'", type);
715 return 0;
716 }
717
718 // Use default LBA 33, same as JMraidcon.
719 // MBR disk: Zero filled if there is no boot code in boot area.
720 // GPT disk: Zero filled if GPT entries 125-128 are empty.
721 unsigned lba = 33;
722
723 unsigned port = ~0;
724 bool force = false;
725 const char * args = type + n1;
726 n1 = -1;
727 sscanf(args, "%u%n", &port, &n1);
728 int n2 = -1, len = strlen(args);
729 if (0 < n1 && n1 < len && sscanf(args + n1, ",s%u%n", &lba, &n2) == 1 && n2 > 0)
730 n1 += n2;
731 n2 = -1;
732 if (0 < n1 && n1 < len && (sscanf(args + n1, ",force%n", &n2), n2) > 0) {
733 force = true;
734 n1 += n2;
735 }
736 if (!(n1 == len && port <= 4 && 1 <= lba && lba <= 255)) {
737 set_err(EINVAL, "Option -d %s,N[,sLBA][,force] must have 0 <= N <= 4 [, 1 <= LBA <= 255]", prefix);
738 return 0;
739 }
740
741 ata_device * jmbdev = new jmb39x::jmb39x_device(this, smartdev, type, version, port, lba, force);
742 // 'smartdev' is now owned by 'jmbdev'
743 smartdev_holder.release();
744 return jmbdev;
745}
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.
virtual bool close()=0
Close device, return false on error.
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.
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:1713
void dStrHex(const uint8_t *up, int len, int no_ascii)
Definition: scsicmds.cpp:368
#define DXFER_FROM_DEVICE
Definition: scsicmds.h:109
#define SCSI_TIMEOUT_DEFAULT
Definition: scsicmds.h:379
#define DXFER_TO_DEVICE
Definition: scsicmds.h:110
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:1347
#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:121
int dxfer_dir
Definition: scsicmds.h:119
size_t cmnd_len
Definition: scsicmds.h:118
size_t dxfer_len
Definition: scsicmds.h:122
uint8_t * cmnd
Definition: scsicmds.h:117
unsigned timeout
Definition: scsicmds.h:126
uint32_t lb_size
Definition: scsicmds.h:174
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:799
bool nonempty(const void *data, int size)
Definition: utility.cpp:682