summaryrefslogtreecommitdiffstats
path: root/src/usr/isteps/nvdimm/nvdimm_update.H
blob: 1e48f7ef88660957579f3904f7650450fe7c33fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/nvdimm/nvdimm_update.H $                       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2018,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#ifndef NVDIMM_UPDATE_H
#define NVDIMM_UPDATE_H

/**
 *  @file nvdimm/nvdimm_update.H
 *
 *  @brief  Interface to support updating NVDIMM controller code
 */

#include <cstdint>
#include <errl/errlentry.H>


namespace NVDIMM
{
// Some invalid constants
const uint16_t INVALID_ID       = 0xFFFF;
const uint16_t INVALID_VERSION  = 0xFFFF;
const uint16_t INVALID_TIMEOUT  = 0xFFFF;
const uint32_t INVALID_TYPE     = 0xFFFFFFFF;

// Type is combination of manufacturer id and product id
const uint32_t SMART_NVDIMM_16GB_TYPE = 0x01945377;
const uint32_t SMART_NVDIMM_32GB_TYPE = 0x01945378;

// LID IDs for each NV controller type
const uint32_t NVDIMM_SIGNATURE_LIDID = 0x80D00025; // ignore this one

// These LIDs are created from these two binaries supplied by SMART
// 16GB image -> src/extucode/NVDIDMM_SRN7A2G4IBM26MP1SC.bin --> 81e00640.lid
// 32GB image -> src/extucode/NVDIDMM_SRN7A4G4IBM24KP2SB.bin --> 81e00641.lid
const uint32_t NVDIMM_16GB_LIDID = 0x81e00640;
const uint32_t NVDIMM_32GB_LIDID = 0x81e00641;

const uint32_t NVDIMM_16GB_BPM_FW_LIDID = 0x81e00642;
const uint32_t NVDIMM_16GB_BPM_CONFIG_LIDID = 0x81e00644;

const uint32_t NVDIMM_32GB_BPM_FW_LIDID = 0x81e00643;
const uint32_t NVDIMM_32GB_BPM_CONFIG_LIDID = 0x81e00645;


enum fw_update_mode : uint8_t
{
    FW_UPDATE_MODE_DISABLED = 0x00,
    FW_UPDATE_MODE_ENABLED = 0x01,
};

class NvdimmLidImage
{
  public:
    /**
     * @brief Constructor that sets access to LID information
     * @param i_lidImageAddr - virtual address where LID was loaded
     * @param i_size - size of the loaded LID
     */
    explicit NvdimmLidImage(const void * i_lidImageAddr, size_t i_size);

    /**
     * @brief Grab the type of the image
     *        (The type will be in raw little-endian format)
     * @return concated manufacturer id and product id
     *         module_mnfg_id_code (0-1) and module_product_id (2-3)
     */
    uint32_t getType();

    /**
     * @brief Get the firmware version of image
     * @return o_version - version of the image in raw little-endian format
     */
    uint16_t getVersion();

    /**
     *  @brief Get the actual NVDIMM flash image to load on the NVDIMM
     *  @return Pointer to the start of the flash image
     */
    const void * getFlashImage();

    /**
     * @brief  Get the size of the actual flash image
     * @return Image size
     */
    size_t getFlashImageSize();


    //-----------------------------------------------------------//
    // Layout of NVDIMM lid image
    //
    // Keeping as struct so it can be overlayed on lid image
    // Note: the multiple byte variables will be in little-endian
    //-----------------------------------------------------------//
    // Header of NVDIMM lid image
    typedef struct nvdimm_image_header
    {
      // Byte 0-1 - 1st part of type
      uint16_t module_mnfg_id_code;
      // Byte 2-3 - 2nd part of type
      uint16_t module_product_id;
      // Byte 4-5
      uint16_t nv_memory_subsys_cntrlr_mnfg_id_code;
      // Byte 6-7
      uint16_t nv_memory_subsys_cntrlr_product_id;
      // Byte 8
      uint8_t  module_revision_code;
      // Byte 9
      uint8_t  nv_memory_subsys_cntrlr_revision_code;
      // Byte 10-11 - this version of code for update
      uint16_t controller_firmware_revision;
      // Byte 12-13
      uint16_t energy_source_firmware_revision;
      // Byte 14
      uint8_t  subcomponent_firmware_revision;
      // Byte 15
      uint8_t  rsvd_0;
      // Byte 16-17
      uint16_t SMART_digital_signature_size;
      // Byte 18
      uint8_t  SMART_digital_signature_type;
      // Byte 19
      union
      {
        uint8_t _valid;
        struct
        {
          uint8_t _subcomponent_firmware_revision:1;          // bit7 in spec
          uint8_t _energy_source_firmware_revision:1;         // bit6
          uint8_t _nv_memory_subsys_cntrlr_revision_code:1;   // bit5
          uint8_t _module_revision_code:1;                    // bit4
          uint8_t _nv_memory_subsys_cntrlr_product_id:1;      // bit3
          uint8_t _nv_memory_subsys_cntrlr_mnfg_id_code:1;    // bit2
          uint8_t _module_mnfg_product_id:1;                  // bit1
          uint8_t _module_mnfg_id:1;                          // bit0 in spec
        } PACKED;
      };

      // Byte 20-23
      uint32_t firmware_image_size; // Includes 32-byte header + actual fw image
                                    // Byte 20 LSB, Byte 23 MSB
      // Byte 24-25
      uint16_t firmware_image_checksum;
      // Byte 26-30
      uint8_t  rsvd_1[5];
      // Byte 31
      uint8_t  firmware_image_format;
    } nvdimm_image_header_t;
    // After header, these two follow:
    // Digital signature (size: SMART_digital_signature_size bytes)
    // Actual flash firmware image
    // (size: firmware_image_size bytes - 32-byte header)
    //-----------------------------------------------------------//

    /**
     * @brief Get a pointer to the header and smart digital signature
     * @param o_size Byte size of the data being returned
     * @return Pointer to 32-byte header + digital signature
     */
    const uint8_t * getHeaderAndSmartSignature(uint16_t & o_size);

  private:
    // force user to supply loaded image addr/size
    NvdimmLidImage();

    // pointer to lid image
    // note: memory is allocated outside of this class,
    // should be left alone
    const void * iv_lidImage;

    // size of lid image
    size_t iv_lidImageSize;
};


class NvdimmInstalledImage
{
  public:
    /**
     * @brief Constructor to associate a target DIMM to grab info from
     * @param i_nvDimm - NVDIMM target
     */
    explicit NvdimmInstalledImage(TARGETING::Target * i_nvDimm);

    /**
     * @brief Grab the type of the installed nvdimm
     *        (The type will be in raw little-endian format)
     * @param o_type - concated manufacturer id and product id
     * @return error if read operation fails
     */
    errlHndl_t getType(uint32_t & o_type);

    /**
     * @brief Grab the installed NVDIMM's version
     * @param o_version - version of installed NVDIMM image (little-endian format)
     * @param i_force_recollect - force hw calls to recollect version
     * @return error if read operation fails
     */
    errlHndl_t getVersion(uint16_t & o_version,
                          const bool i_force_recollect = false);

    /**
     * @brief Read the current slot that is running
     * @param o_slot - 0 or 1
     * @return error if read operation fails
     */
    errlHndl_t getRunningSlot(uint8_t & o_slot);

    /**
     * @brief Accessor to grab the current NVDIMM target
     * @return NVDIMM target
     */
    TARGETING::Target * getNvdimmTarget(void)
    {
        return iv_dimm;
    }

    /**
     * @brief Accessor to grab the amount of retries it took to write regions
     * @return Cumulative total region write retries
     */
    uint8_t getRegionWriteRetries(void)
    {
        return iv_region_write_retries;
    }

    /**
     * @brief Update the current NV Controller
     * @param Update using this image
     * @return error pointer if failure to update, else nullptr
     */
    errlHndl_t updateImage(NvdimmLidImage * i_lidImage);

  private:
      // nvdimm target
      TARGETING::Target * iv_dimm;

      // little-endian version of installed nvdimm
      uint16_t iv_version;

      // Type contains these two concatentated little-endian IDs
      uint16_t iv_manufacturer_id;
      uint16_t iv_product_id;

      // timeout value (in seconds) for FIRMWARE_OPS_TIMEOUT0/1
      uint16_t iv_timeout;

      // constant for invalid setting
      const uint8_t INVALID_REGION_BLOCK_SIZE = 0x00;

      // maximum blocks allowed per region (REGION_BLOCK_SIZE)
      uint8_t iv_max_blocks_per_region;

      // set to true when doing update
      bool iv_fw_update_mode_enabled;

      // retry attempts for all regions
      uint8_t iv_region_write_retries;


      // Helper functions for updating the installed lid
      /**
       * @brief Transfer a region of bytes in multiple 32-byte blocks
       * @param i_data - data to transfer
       * @param i_data_size - size of the data to transfer
       * @param i_use_region_block_size - use the register REGION_BLOCK_SIZE
       *                                  as number of blocks to send
       * @return error if transfer fails, else nullptr
       */
      errlHndl_t byteRegionBlockTransfer(const uint8_t * i_data,
                                         const uint16_t i_data_size,
                                         bool i_use_region_block_size = true);

      /**
       * @brief NV controller calculate checksum of region of data bytes
       * @param o_nvCksm - NV calculated checksum
       * @return error if unable to get checksum, else nullptr
       */
      errlHndl_t calcAndGetCksm(uint16_t & o_nvCksm);

      /**
       * @brief Change the Firmware Update Mode
       * @param i_mode - enable or disable mode
       * @return error if unable to change mode, else nullptr
       */
      errlHndl_t changeFwUpdateMode(fw_update_mode i_mode);

      /**
       * @brief Clear the Firmware Data Block
       * @return error if unable to clear data block, else nullptr
       */
      errlHndl_t clearFwDataBlock();

      /**
       * @brief Clear the Firmware Operations Status
       * @return error if unable to clear status, else nullptr
       */
      errlHndl_t clearFwOpsStatus();

      /**
       * @brief Commit the Firmware Region Operation
       * @return error if unable commit, else nullptr
       */
      errlHndl_t commitFwRegion();

      /**
       * @brief Get the number of blocks per region
       * @param io_blocks_per_region - value of REGION_BLOCK_SIZE register
       * @return error if unable to get value, else nullptr
       */
      errlHndl_t getBlocksPerRegion(uint8_t & io_blocks_per_region);

      /**
       * @brief Get the Firmware Operations Timeout
       * @param o_timeout - timeout in seconds for a Firmware Operation
       *                    to complete
       * @return error if unable to get timeout value, else nullptr
       */
      errlHndl_t getFwOpsTimeout(uint16_t & o_timeout);

      /**
       * @brief Is the Firmware Operation successful?
       * @param o_success - true if FIRMWARE_OPS_STATUS reporting success
       * @return error if unable to read register, else nullptr
       */
      errlHndl_t isFwOpsSuccess(bool & o_success);

      /**
       * @brief Updates the NV controller with the lid's image data
       *        (minus header and signature)
       * @param i_lidImage - lid object with image data
       * @return error if unable to update, else nullptr
       */
      errlHndl_t updateImageData(NvdimmLidImage * i_lidImage);

      /**
       * @brief Run FW operation to validate the header and verify
       *        it was a successful operation
       * @return error if unable to validate, else nullptr
       */
      errlHndl_t validateFwHeader();

      /**
       * @brief Run FW operation to validate the firmware image and verify
       *        it was a successful operation
       * @return error if unable to validate, else nullptr
       */
      errlHndl_t validateFwImage();

      /**
       * @brief Wait until FW operation is no longer in progress
       * @return error if failed i2c operation getting status or if
       *         timeout happened before status indicated operation completion,
       *         else nullptr
       */
      errlHndl_t waitFwOpsComplete();

      /**
       * @brief Wait until FW operation reports data block was received
       *        Polls FIRMWARE_OPS_STATUS offset for FIRMWARE_BLOCK_RECEIVED
       * @return error if failed i2c operation getting status or if
       *         timeout happened before status indicated operation completion,
       *         else nullptr
       */
      errlHndl_t waitFwOpsBlockReceived();

      /**
       * @brief Checksum calculation
       *        See JESD245B for documentation of this checksum
       * @param i_data - pointer to data
       * @param i_data_size - size of data being pointed too
       * @return checksum
       */
      uint16_t crc16(const uint8_t * i_data, int i_data_size);
};


class NvdimmsUpdate
{
  public:
    /**
     *  @brief Constructor that uses passed in nvdimm list
     *  @parm[in] i_nvdimmList List of NVDIMMs to update
     */
    explicit NvdimmsUpdate(TARGETING::TargetHandleList i_nvdimmList);

    /**
     *  @brief Main function that tries to update all NVDIMMs (if needed)
     *         This function runs SPD and lid loading/unloading, so should
     *         not be called multiple times
     *  @return true if no errors reported, else false
     */
    bool runUpdate(void);

  protected:
    /**
     *  @brief Checks if an update is needed for an individual nvdimm
     *  @param[out] o_update_needed -
     *      true -  lid image is good to update current image
     *      false - update not needed
     *  @param[in] i_lid_image - lid image that is appropriate for nvdimm update
     *  @param[in] i_cur_image - current installed image on the nvdimm
     *  @return error if failure during check, else false
     */
    errlHndl_t isUpdateNeeded(bool & o_update_needed,
                              NvdimmLidImage * i_lid_image,
                              NvdimmInstalledImage * i_cur_image);

    /**
     * @brief Update the list of NVDIMMs with the supplied LID image
     * @param[in] i_lidImage - LID image for update
     * @param[in] i_list - List of installed NVDIMMs to update
     * @return true if no errors reported, else false
     */
    bool runUpdateUsingLid(NvdimmLidImage * i_lidImage,
                           std::vector<NvdimmInstalledImage *> &i_list);

  private:
    // Force user to supply NVDIMM list
    NvdimmsUpdate();

    // List of NVDIMMs installed in system
    TARGETING::TargetHandleList iv_nvdimmList;

};

} // Namespace NVDIMM
#endif
OpenPOWER on IntegriCloud