1 /*
2  * dev_intelliprop.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2016 Casey Biemiller  <cbiemiller@intelliprop.com>
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "config.h"
12 
13 #include "atacmds.h" // ATTR_PACKED, STATIC_ASSERT, ata_debugmode
14 #include "dev_interface.h"
15 #include "dev_tunnelled.h"
16 #include <errno.h>
17 
18 const char * dev_intelliprop_cpp_cvsid = "$Id: dev_intelliprop.cpp 5089 2020-10-06 15:31:47Z chrfranke $";
19 
20 //Vendor Specific log addresses
21 #define LOG_C0           0xc0
22 
23 // VS LOG MODE CONTROL BITS
24 enum {
25   IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED   = (0 << 0), // NOTE: Not supported
26   IPROP_VS_LOG_MODE_CTL_MANUAL_SUPPORTED = (1 << 1),
27   IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED     = (0 << 2), // NOTE: Not supported
28   IPROP_VS_LOG_MODE_CTL_MANUAL_ENABLED   = (1 << 3),
29 };
30 
31 // VS LOG PORT SETTING BITS
32 enum {
33   IPROP_VS_LOG_PORT_WRITE_ENABLE_MASK  = 0xC000,
34   IPROP_VS_LOG_PORT_WRITE_ENABLE_VALID = 0x8000,
35   IPROP_VS_LOG_PORT_RX_DC_GAIN_MASK    = 0x3000,
36   IPROP_VS_LOG_PORT_RX_DC_GAIN_SHIFT   = 12,
37   IPROP_VS_LOG_PORT_RX_EQ_MASK         = 0x0F00,
38   IPROP_VS_LOG_PORT_RX_EQ_SHIFT        = 8,
39   IPROP_VS_LOG_PORT_TX_PREEMP_MASK     = 0x00F8,
40   IPROP_VS_LOG_PORT_TX_PREEMP_SHIFT    = 3,
41   IPROP_VS_LOG_PORT_TX_VOD_MASK        = 0x0007,
42   IPROP_VS_LOG_PORT_TX_VOD_SHIFT       = 0,
43 };
44 
45 //This struct is used for the Vendor Specific log C0 on devices that support it.
46 #pragma pack(1)
47 struct iprop_internal_log
48 {
49   uint32_t drive_select;       // Bytes - [  3:  0] of Log C0
50   uint32_t obsolete;           // Bytes - [  7:  4] of Log C0
51   uint8_t  mode_control;       // Byte  - [      8] of Log C0
52   uint8_t  log_passthrough;    // Byte  - [      9] of Log C0
53   uint16_t tier_id;            // Bytes - [ 11: 10] of Log C0
54   uint32_t hw_version;         // Bytes - [ 15: 12] of Log C0
55   uint32_t fw_version;         // Bytes - [ 19: 16] of Log C0
56   uint8_t  variant[8];         // Bytes - [ 27: 20] of Log C0
57   uint8_t  reserved[228];      // Bytes - [255: 28] of Log C0
58   uint16_t port_0_settings[3]; // Bytes - [263:256] of Log C0
59   uint16_t port_0_reserved;
60   uint16_t port_1_settings[3]; // Bytes - [271:264] of Log C0
61   uint16_t port_1_reserved;
62   uint16_t port_2_settings[3]; // Bytes - [279:272] of Log C0
63   uint16_t port_2_reserved;
64   uint16_t port_3_settings[3]; // Bytes - [287:280] of Log C0
65   uint16_t port_3_reserved;
66   uint16_t port_4_settings[3]; // Bytes - [295:288] of Log C0
67   uint16_t port_4_reserved;
68   uint8_t  reserved2[214];     // Bytes - [509:296] of Log C0
69   uint16_t crc;                // Bytes - [511:510] of Log C0
70 } ATTR_PACKED;
71 #pragma pack()
72 STATIC_ASSERT(sizeof(iprop_internal_log) == 512);
73 
74 /**
75  * buffer is a pointer to a buffer of bytes, which should include data and
76  *   also CRC if the function is being used to check CRC
77  * len is the number of bytes in the buffer (including CRC if it is present)
78  * check_crc is a boolean value, set true to check an existing CRC, false
79  *   to calculate a new CRC
80  */
iprop_crc16_1(uint8_t * buffer,uint32_t len,bool check_crc)81 static uint16_t iprop_crc16_1(uint8_t * buffer, uint32_t len, bool check_crc)
82 {
83   uint8_t crc[16];
84   uint16_t crc_final = 0;
85   uint8_t crc_msb;
86   uint8_t data_msb;
87   uint32_t total_len;
88 
89   // Initialize CRC array
90   for (uint32_t ii = 0; ii < 16; ii++) {
91     crc[ii] = 0;
92     //crc[ii] = (crc_in >> ii) & 1;
93   }
94 
95   // If calculating a new CRC, we need to pad the data with extra zeroes
96   total_len = check_crc ? len : len + 2;
97 
98   // Loop for each byte, plus extra for the CRC itself
99   for (uint32_t ii = 0; ii < total_len; ii++) {
100     uint8_t data = (ii < len) ? buffer[ii] : 0;
101 
102     // Loop for each bit
103     for (uint32_t jj = 0; jj < 8; jj++) {
104       crc_msb = crc[15];
105       data_msb = (data >> (8 - jj - 1)) & 1;
106 
107       crc[15] = crc[14] ^ crc_msb;
108       crc[14] = crc[13];
109       crc[13] = crc[12];
110       crc[12] = crc[11];
111       crc[11] = crc[10] ^ crc_msb;
112       crc[10] = crc[9];
113       crc[9] = crc[8] ^ crc_msb;
114       crc[8] = crc[7] ^ crc_msb;
115       crc[7] = crc[6] ^ crc_msb;
116       crc[6] = crc[5];
117       crc[5] = crc[4] ^ crc_msb;
118       crc[4] = crc[3] ^ crc_msb;
119       crc[3] = crc[2];
120       crc[2] = crc[1] ^ crc_msb;
121       crc[1] = crc[0] ^ crc_msb;
122       crc[0] = data_msb ^ crc_msb;
123     }
124   }
125 
126   // Convert CRC array to final value
127   for (uint32_t ii = 0; ii < 16; ii++) {
128     if (crc[ii] == 1) {
129       crc_final |= (1 << ii);
130     } else {
131       crc_final &= ~(1 << ii);
132     }
133   }
134 
135   return crc_final;
136 }
137 
iprop_dump_log_structure(struct iprop_internal_log const * const log)138 static void iprop_dump_log_structure(struct iprop_internal_log const * const log)
139 {
140   pout("Dumping LOG Structure:\n");
141   pout("  drive_select:           0x%08x\n", log->drive_select);
142   pout("  obsolete:               0x%08x\n", log->obsolete);
143   pout("  mode_control:           0x%02x\n", log->mode_control);
144   pout("  log_passthrough:        0x%02x\n", log->log_passthrough);
145   pout("  tier_id:                0x%04x\n", log->tier_id);
146   pout("  hw_version:             0x%08x\n", log->hw_version);
147   pout("  fw_version:             0x%08x\n", log->fw_version);
148   pout("  variant:                \"");
149   for (int ii = 0; ii < 8; ii++) {
150     pout("%c", (char)log->variant[ii]);
151   }
152   pout("\"\n");
153   pout("  port_0_settings(Gen 1): 0x%08x\n", log->port_0_settings[0]);
154   pout("  port_0_settings(Gen 2): 0x%08x\n", log->port_0_settings[1]);
155   pout("  port_0_settings(Gen 3): 0x%08x\n", log->port_0_settings[2]);
156   pout("  port_1_settings(Gen 1): 0x%08x\n", log->port_1_settings[0]);
157   pout("  port_1_settings(Gen 2): 0x%08x\n", log->port_1_settings[1]);
158   pout("  port_1_settings(Gen 3): 0x%08x\n", log->port_1_settings[2]);
159   pout("  port_2_settings(Gen 1): 0x%08x\n", log->port_2_settings[0]);
160   pout("  port_2_settings(Gen 2): 0x%08x\n", log->port_2_settings[1]);
161   pout("  port_2_settings(Gen 3): 0x%08x\n", log->port_2_settings[2]);
162   pout("  port_3_settings(Gen 1): 0x%08x\n", log->port_3_settings[0]);
163   pout("  port_3_settings(Gen 2): 0x%08x\n", log->port_3_settings[1]);
164   pout("  port_3_settings(Gen 3): 0x%08x\n", log->port_3_settings[2]);
165   pout("  port_4_settings(Gen 1): 0x%08x\n", log->port_4_settings[0]);
166   pout("  port_4_settings(Gen 2): 0x%08x\n", log->port_4_settings[1]);
167   pout("  port_4_settings(Gen 3): 0x%08x\n", log->port_4_settings[2]);
168   pout("  crc:                    0x%04x\n", log->crc);
169   pout("\n");
170 }
171 
iprop_switch_routed_drive(ata_device * device,int drive_select)172 static bool iprop_switch_routed_drive(ata_device * device, int drive_select)
173 {
174   // Declare a log page buffer and initialize it with what is on the drive currently
175   iprop_internal_log write_payload;
176   if (!ataReadLogExt(device, LOG_C0, 0, 0, &write_payload, 1))
177     return device->set_err(EIO, "intelliprop: Initial Read Log failed: %s", device->get_errmsg());
178 
179   // Check the returned data is good
180   uint16_t const crc_check = iprop_crc16_1((uint8_t *)&write_payload,
181                                            sizeof(struct iprop_internal_log),
182                                            false);
183 
184 
185    //If this first read fails the crc check, the log can be still sent with routing information
186    //as long as everything else in the log is zeroed. So there is no need to return false.
187   if (crc_check != 0) {
188     if (ata_debugmode)
189       pout("Intelliprop WARNING: Received log crc(0x%04X) is invalid!\n", crc_check);
190     iprop_dump_log_structure(&write_payload);
191     memset(&write_payload, 0, sizeof(struct iprop_internal_log));
192   }
193 
194   //The option to read the log, even if successful, could be useful
195   if (ata_debugmode)
196     iprop_dump_log_structure(&write_payload);
197 
198   // Modify the current drive select to what we were given
199   write_payload.drive_select = (uint32_t)drive_select;
200   if (ata_debugmode)
201     pout("Intelliprop - Change to port 0x%08X.\n", write_payload.drive_select);
202   write_payload.log_passthrough = 0; // TEST (Set to 1, non hydra member drive will abort --> test error handling)
203   write_payload.tier_id = 0; // TEST (Set to non-zero, non hydra member drive will abort --> test error handling)
204 
205   // Update the CRC area
206   uint16_t const crc_new = iprop_crc16_1((uint8_t *)&write_payload,
207                                          sizeof(struct iprop_internal_log) - sizeof(uint16_t),
208                                          false);
209   write_payload.crc = (crc_new >> 8) | (crc_new << 8);
210 
211   // Check our CRC work
212   uint16_t const crc_check2 = iprop_crc16_1((uint8_t *)&write_payload,
213                                             sizeof(struct iprop_internal_log),
214                                             false);
215   if (crc_check2 != 0)
216     return device->set_err(EIO, "intelliprop: Re-calculated log crc(0x%04X) is invalid!", crc_check2);
217 
218   // Apply the Write LOG
219   if (!ataWriteLogExt(device, LOG_C0, 0, &write_payload, 1))
220     return device->set_err(EIO, "intelliprop: Write Log failed: %s", device->get_errmsg());
221 
222   // Check that the Write LOG was applied
223   iprop_internal_log check_payload;
224   if (!ataReadLogExt(device, LOG_C0, 0, 0, &check_payload, 1))
225     return device->set_err(EIO, "intelliprop: Secondary Read Log failed: %s", device->get_errmsg());
226 
227   if (check_payload.drive_select != write_payload.drive_select) {
228     if (ata_debugmode > 1)
229       iprop_dump_log_structure(&check_payload);
230     return device->set_err(EIO, "intelliprop: Current drive select val(0x%08X) is not expected(0x%08X)",
231          check_payload.drive_select,
232          write_payload.drive_select);
233   }
234 
235   return true;
236 }
237 
238 namespace intelliprop {
239 
240 class intelliprop_device
241 : public tunnelled_device<
242     /*implements*/ ata_device,
243     /*by using an*/ ata_device
244   >
245 {
246 public:
247   intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev);
248 
249   virtual ~intelliprop_device();
250 
251   virtual bool open();
252 
253   virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
254 
255 private:
256   unsigned m_phydrive;
257 };
258 
259 
intelliprop_device(smart_interface * intf,unsigned phydrive,ata_device * atadev)260 intelliprop_device::intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev)
261 : smart_device(intf, atadev->get_dev_name(), "intelliprop", "intelliprop"),
262   tunnelled_device<ata_device, ata_device>(atadev),
263   m_phydrive(phydrive)
264 {
265   set_info().info_name = strprintf("%s [intelliprop_disk_%u]", atadev->get_info_name(), phydrive);
266 }
267 
~intelliprop_device()268 intelliprop_device::~intelliprop_device()
269 {
270 }
271 
open()272 bool intelliprop_device::open()
273 {
274   if (!tunnelled_device<ata_device, ata_device>::open())
275     return false;
276 
277   ata_device * atadev = get_tunnel_dev();
278   if (!iprop_switch_routed_drive(atadev, m_phydrive)) {
279     close();
280     return set_err(atadev->get_err());
281   }
282 
283   return true;
284 }
285 
ata_pass_through(const ata_cmd_in & in,ata_cmd_out & out)286 bool intelliprop_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
287 {
288   return get_tunnel_dev()->ata_pass_through(in, out);
289 }
290 }//namespace
291 
get_intelliprop_device(const char * type,ata_device * atadev)292 ata_device * smart_interface::get_intelliprop_device(const char * type, ata_device * atadev)
293 {
294   // Take temporary ownership of 'atadev' to delete it on error
295   ata_device_auto_ptr atadev_holder(atadev);
296 
297   unsigned phydrive = ~0; int n = -1;
298   sscanf(type, "intelliprop,%u%n", &phydrive, &n);
299   if (!(n == (int)strlen(type) && phydrive <= 3)) {
300     set_err(EINVAL, "Option '-d intelliprop,N' must have 0 <= N <= 3");
301     return 0;
302   }
303 
304   ata_device * itldev = new intelliprop::intelliprop_device(this, phydrive, atadev);
305   // 'atadev' is now owned by 'itldev'
306   atadev_holder.release();
307   return itldev;
308 }
309