-
Notifications
You must be signed in to change notification settings - Fork 97
/
nvdimm_update.C
2201 lines (2042 loc) · 87.4 KB
/
nvdimm_update.C
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
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* IBM_PROLOG_BEGIN_TAG */
/* This is an automatically generated prolog. */
/* */
/* $Source: src/usr/isteps/nvdimm/nvdimm_update.C $ */
/* */
/* 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 */
#include "nvdimm_update.H"
#include "nvdimm.H"
#include <isteps/nvdimm/nvdimm.H>
#include <isteps/nvdimm/nvdimmreasoncodes.H>
#include <initservice/istepdispatcherif.H> // sendProgressCode
#include <util/utilmclmgr.H> // secure LID manager
#include <errl/errlmanager.H>
#include <devicefw/userif.H>
#include <vpd/spdenums.H>
#include <sys/time.h>
// Unique tracing for nvdimm update process
const char NVDIMM_UPD[] = "NVDIMM_UPD";
trace_desc_t* g_trac_nvdimm_upd = NULL;
TRAC_INIT(&g_trac_nvdimm_upd, NVDIMM_UPD, 2*KILOBYTE);
// Easy macro replace for unit testing
//#define TRACUCOMP(args...) TRACFCOMP(args)
#define TRACUCOMP(args...)
namespace NVDIMM
{
//////////////////////////////////////////////////////////////////////////////
// Helper Inline functions
//////////////////////////////////////////////////////////////////////////////
/**
* @brief Inline function to collect NVDIMM traces and
* make sure error is logged at least as PREDICTIVE
*/
inline void commitPredictiveNvdimmError(errlHndl_t & io_err)
{
io_err->collectTrace(NVDIMM_UPD, 512);
io_err->collectTrace(NVDIMM_COMP_NAME, 256); // for helper function traces
if ( io_err->sev() < ERRORLOG::ERRL_SEV_PREDICTIVE )
{
io_err->setSev( ERRORLOG::ERRL_SEV_PREDICTIVE );
}
ERRORLOG::errlCommit(io_err, NVDIMM_COMP_ID);
}
////////////////////////////////////////////////////////////////////////////////
// Helper structs/enums for code update
//
// Refer to JEDEC BAEBI spec for details
// https://www.jedec.org/standards-documents/docs/jesd245a
////////////////////////////////////////////////////////////////////////////////
// Definition of FIRMWARE_OPS_STATUS -- offset 0x71
typedef union {
uint8_t whole;
struct
{
// [7:6] : reserved
uint8_t reserved : 2;
// [5] : the abort of the last fw operation failed
uint8_t fw_ops_abort_error : 1;
// [4] : the last fw operation was aborted by the host
uint8_t fw_ops_abort_success : 1;
// [3] : the last block has been received successfully by the NVDIMM
// and the host may proceed with sending the next block
uint8_t fw_ops_block_received : 1;
// [2] : the module is in FW update mode where firmware on the module
// can be changed.
// If cleared, firmware on the module cannot be changed
uint8_t fw_ops_update_mode : 1;
// [1] : the last firmware operation failed
uint8_t fw_ops_error : 1;
// [0] : the last firmware operation completed without any errors
uint8_t fw_ops_success : 1;
} PACKED;
} fw_ops_status_t;
// Definition of FIRMWARE_OPS_CMD -- offset 0x4A
typedef union {
uint8_t whole;
struct
{
// [6-7] : Reserved for future use
uint8_t reserved : 2;
// [5] : Start a Validate Firmware Image operation
uint8_t start_validate_fw_image_op : 1;
// [4] : Start a Validate Firmware Header operation
uint8_t start_validate_fw_header_op : 1;
// [3] : Start a Commit Firmware operation
uint8_t start_commit_fw_op : 1;
// [2] : Start a Generate Firmware Checksum operation
uint8_t start_generate_fw_checksum_op : 1;
// [1] : Start a Clear Firmware operation
uint8_t start_clear_fw_op : 1;
// [0] : Enable / Disable firmware update mode.
// 0b0: fw update mode is disabled.
// 0b1: fw update mode is enabled.
uint8_t firmware_update_mode : 1;
} PACKED;
} fw_ops_cmd_t;
// Definition of NVDIMM_CMD_STATUS0 -- offset 0x61
typedef union {
uint8_t whole;
struct
{
// [7] : Firmware operations currently in progress
uint8_t firmware_ops_in_progress : 1;
// [6] : Arm operation in progress
uint8_t arm_in_progress : 1;
// [5] : Abort operation in progress
uint8_t abort_in_progress : 1;
// [4] : Erase operation in progress
uint8_t erase_in_progress : 1;
// [3] : Restore operation in progress
uint8_t restore_in_progress : 1;
// [2] : Catastrophic Save operation in progress
uint8_t catastrophic_save_in_progress : 1;
// [1] : Factory Default in progress
uint8_t factory_default_in_progress : 1;
// [0] : Operation in progress
uint8_t operation_in_progress : 1;
} PACKED;
} nvdimm_cmd_status0_t;
// A code update block is composed of this many bytes
const uint8_t BYTES_PER_BLOCK = 32;
///////////////////////////////////////////////////////////////////////////////
// NVDIMM LID Image
///////////////////////////////////////////////////////////////////////////////
NvdimmLidImage::NvdimmLidImage(const void * i_lidImageAddr, size_t i_size) :
iv_lidImage(i_lidImageAddr), iv_lidImageSize(i_size)
{
}
uint32_t NvdimmLidImage::getType()
{
uint32_t o_type = INVALID_TYPE;
if (iv_lidImageSize >= sizeof(nvdimm_image_header_t))
{
const nvdimm_image_header_t * pLid =
reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage);
o_type = (uint32_t)pLid->module_mnfg_id_code << 16;
o_type |= (uint32_t)pLid->module_product_id;
}
return o_type;
}
uint16_t NvdimmLidImage::getVersion()
{
uint16_t o_version = INVALID_VERSION;
if (iv_lidImageSize >= sizeof(nvdimm_image_header_t))
{
const nvdimm_image_header_t * pLid =
reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage);
o_version = pLid->controller_firmware_revision;
}
return o_version;
}
const uint8_t * NvdimmLidImage::getHeaderAndSmartSignature(uint16_t & o_size)
{
o_size = 0;
if (iv_lidImageSize > sizeof(nvdimm_image_header_t))
{
const nvdimm_image_header_t * pLid =
reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage);
o_size = le16toh(pLid->SMART_digital_signature_size);
o_size += sizeof(nvdimm_image_header_t);
}
return reinterpret_cast<const uint8_t*>(iv_lidImage);
}
const void * NvdimmLidImage::getFlashImage()
{
void * o_image_ptr = nullptr;
if (iv_lidImageSize > sizeof(nvdimm_image_header_t))
{
const nvdimm_image_header_t * pLid =
reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage);
// make sure we don't point outside of lid memory
// nvdimm flash image starts after the header and digital signature
if ((sizeof(nvdimm_image_header_t) +
le16toh(pLid->SMART_digital_signature_size)) < iv_lidImageSize)
{
TRACUCOMP(g_trac_nvdimm_upd,
"NvdimmLidImage::getFlashImage() -> starts at offset 0x%X "
"(digital signature size: %d, header size: %d)",
sizeof(nvdimm_image_header_t) +
le16toh(pLid->SMART_digital_signature_size),
le16toh(pLid->SMART_digital_signature_size),
sizeof(nvdimm_image_header_t));
o_image_ptr = reinterpret_cast<void*>(const_cast<uint8_t *>(
reinterpret_cast<const uint8_t*>(iv_lidImage) +
sizeof(nvdimm_image_header_t) +
le16toh(pLid->SMART_digital_signature_size)));
}
}
return o_image_ptr;
}
size_t NvdimmLidImage::getFlashImageSize()
{
uint32_t o_flash_size = 0;
if (iv_lidImageSize > sizeof(nvdimm_image_header_t))
{
const nvdimm_image_header_t * pLid =
reinterpret_cast<const nvdimm_image_header_t*>(iv_lidImage);
// Note: firmware_image_size does NOT include the Digital Signature
// (does include the 32-byte header though)
o_flash_size = le32toh(pLid->firmware_image_size);
o_flash_size -= sizeof(nvdimm_image_header_t);
// safety check so we don't access past lid's memory size
if ((o_flash_size + sizeof(nvdimm_image_header_t) +
le16toh(pLid->SMART_digital_signature_size)) > iv_lidImageSize)
{
TRACFCOMP(g_trac_nvdimm_upd,
ERR_MRK"getFlashImageSize(): %ld flash size + %ld header + "
"%ld digital signature is greater than %ld overall lid size",
o_flash_size, sizeof(nvdimm_image_header_t),
le16toh(pLid->SMART_digital_signature_size),
iv_lidImageSize);
// flash image size is outside of lid memory bounds so don't return
// a valid flash size
o_flash_size = 0;
}
}
return o_flash_size;
}
///////////////////////////////////////////////////////////////////////////////
// NVDIMM Installed Image
///////////////////////////////////////////////////////////////////////////////
NvdimmInstalledImage::NvdimmInstalledImage(TARGETING::Target * i_nvDimm) :
iv_dimm(i_nvDimm), iv_version(INVALID_VERSION),
iv_manufacturer_id(INVALID_ID), iv_product_id(INVALID_ID),
iv_timeout(INVALID_TIMEOUT),
iv_max_blocks_per_region(INVALID_REGION_BLOCK_SIZE)
{
// initialize to invalid values
}
errlHndl_t NvdimmInstalledImage::getType(uint32_t & o_type)
{
errlHndl_t l_err = nullptr;
do {
size_t l_id_size = 0; // size of id
if ( iv_manufacturer_id == INVALID_ID)
{
// grab values for the installed NVDIMM via SPD
l_id_size = sizeof(iv_manufacturer_id);
l_err = deviceRead(iv_dimm, &iv_manufacturer_id, l_id_size,
DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_MANUFACTURER_ID));
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::getType()"
" nvdimm[%X] failed to read manufacturer ID",
TARGETING::get_huid(iv_dimm));
iv_manufacturer_id = INVALID_ID;
break;
}
}
if (iv_product_id == INVALID_ID)
{
// grab values for the installed NVDIMM via SPD
l_id_size = sizeof(iv_product_id);
l_err = deviceRead(iv_dimm, &iv_product_id, l_id_size,
DEVICE_SPD_ADDRESS(SPD::RAW_MODULE_PRODUCT_ID));
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::getType()"
" nvdimm[%X] failed to read product ID",
TARGETING::get_huid(iv_dimm));
iv_product_id = INVALID_ID;
break;
}
}
} while (0);
// return the concatenated Type (this may include INVALID_IDs)
o_type = ((uint32_t)iv_manufacturer_id << 16) | (uint32_t)iv_product_id;
return l_err;
}
errlHndl_t NvdimmInstalledImage::getVersion(uint16_t & o_version,
const bool i_force_recollect)
{
errlHndl_t l_err = nullptr;
do {
if ((iv_version == INVALID_VERSION) || i_force_recollect)
{
// Return version in little-endian format
uint8_t l_rev1 = 0xFF;
uint8_t l_rev0 = 0xFF;
l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV0, l_rev0 );
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::"
"getVersion() nvdimm[%X] failed to read SLOT1_FWREV0",
TARGETING::get_huid(iv_dimm));
iv_version = INVALID_VERSION;
break;
}
iv_version = (uint16_t)l_rev0 << 8;
l_err = nvdimmReadReg(iv_dimm, SLOT1_FWREV1, l_rev1 );
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"NvdimmInstalledImage::"
"getVersion() nvdimm[%X] failed to read SLOT1_FWREV1",
TARGETING::get_huid(iv_dimm));
iv_version = INVALID_VERSION;
break;
}
iv_version |= (uint16_t)l_rev1;
}
} while (0);
o_version = iv_version;
return l_err;
}
errlHndl_t NvdimmInstalledImage::updateImage(NvdimmLidImage * i_lidImage)
{
errlHndl_t l_err = nullptr;
// need to always disable this after it gets enabled
bool l_fw_update_mode_enabled = false;
do {
INITSERVICE::sendProgressCode();
////////////////////////////////////////////////////////////////////////
// Start of firmware update logic, section 9.7 in JESD245B
////////////////////////////////////////////////////////////////////////
// 1. Validate module manufacturer ID and module product identifier
// Done before this was called, it is what selected i_lidImage
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 2");
// 2. Verify 'Operation In Progress' bit in the NVDIMM_CMD_STATUS0
// register is cleared (ie. NV controller is NOT busy)
nvdimm_cmd_status0_t l_status;
l_err = nvdimmReadReg(iv_dimm, NVDIMM_CMD_STATUS0, l_status.whole);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Read of NVDIMM_CMD_STATUS0 register on 0x%.8X NVDIMM failed",
TARGETING::get_huid(iv_dimm), l_status.whole);
break;
}
if (l_status.operation_in_progress)
{
TRACFCOMP(g_trac_nvdimm_upd,ERR_MRK"updateImage: "
"NV controller is busy (0x%08X) for NVDIMM 0x%.8X",
l_status.whole, TARGETING::get_huid(iv_dimm));
/*
*@errortype
*@moduleid UPDATE_IMAGE
*@reasoncode NVDIMM_OPERATION_IN_PROGRESS
*@userdata1 NVDIMM Target Huid
*@userdata2 NVDIMM_CMD_STATUS0
*@devdesc NV controller is busy so no update can run
*@custdesc NVDIMM not updated
*/
l_err = new ERRORLOG::ErrlEntry(
ERRORLOG::ERRL_SEV_PREDICTIVE,
UPDATE_IMAGE,
NVDIMM_OPERATION_IN_PROGRESS,
TARGETING::get_huid(iv_dimm),
l_status.whole,
ERRORLOG::ErrlEntry::NO_SW_CALLOUT );
l_err->collectTrace( NVDIMM_COMP_NAME, 256 );
l_err->addPartCallout( iv_dimm,
HWAS::NV_CONTROLLER_PART_TYPE,
HWAS::SRCI_PRIORITY_HIGH );
l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE,
HWAS::SRCI_PRIORITY_LOW );
break;
}
// 3. Make sure we start from a cleared state
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 3");
l_err = clearFwOpsStatus();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Unable to clear firmware ops status for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 4. Enable firmware update mode
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 4");
l_err = changeFwUpdateMode(FW_UPDATE_MODE_ENABLED);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Unable to enable firmware update mode for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// Set this flag so we will disable the update mode on error
l_fw_update_mode_enabled = true;
// 5. Clear the Firmware Operation status
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 5");
l_err = clearFwOpsStatus();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Unable to clear firmware ops status for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 6. Clear the firmware data block to ensure there is no residual data.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 6");
l_err = clearFwDataBlock();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Step 6. clearFwDataBlock() failed for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 7. Send the first part (header + SMART signature) of
// the Firmware Image Data
// 7a. Write the TYPED_BLOCK_DATA register with 0x1 (Firmware Image Data)
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7a");
l_err = nvdimmWriteReg(iv_dimm, TYPED_BLOCK_DATA, 0x01);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Write of 0x01 to TYPED_BLOCK_DATA failed for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 7b. Write the BLOCK_ID, REGION_ID0 and REGION_ID1 registers with value 0.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7b");
l_err = nvdimmWriteReg(iv_dimm, BLOCK_ID, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Write of 0x00 to BLOCK_ID failed for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Write of 0x00 to REGION_ID0 failed for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Write of 0x00 to REGION_ID1 failed for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 7c. Send the header (first 32 bytes of the image) +
// SMART digital signature to multiple blocks of
// TYPED_BLOCK_DATA_BYTE0 - TYPED_BLOCK_DATA_BYTE31.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7c");
uint16_t header_plus_signature_size = 0;
const uint8_t * pHeaderAndDigitalSignature =
i_lidImage->getHeaderAndSmartSignature(header_plus_signature_size);
l_err = byteRegionBlockTransfer( pHeaderAndDigitalSignature,
header_plus_signature_size, false );
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: Unable to send "
"header and digital signature update for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 7d. Validate the transfer by checksum
// 7d.i. Host calculate checksum using the crc16 algo in JESD245B
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.i.");
uint16_t hostCksm = crc16(pHeaderAndDigitalSignature,
header_plus_signature_size);
// 7d.ii. Set bit 1 (Clear the FIRMWARE_OPS_STATUS register) in
// the NVDIMM_MGT_CMD1 register.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.ii.");
l_err = clearFwOpsStatus();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"Unable to clear Firmware Operations Status for NVDIMM %.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// 7d.iii - 7d.vii
// Write the FIRMWARE_OPS_CMD register with value 0x04
// to start a Generate Firmware Checksum operation.
// Compare the module generated value with the host value.
// Abort the workflow if they do not match.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 7d.iii.");
uint16_t nvCksm;
l_err = calcAndGetCksm(nvCksm);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: calcAndGetCksm() "
"failed for 0x%.8X nvdimm", TARGETING::get_huid(iv_dimm));
break;
}
if (hostCksm != nvCksm)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImage: "
"NVDIMM 0x%.8X: data checksums mismatch (calc host: 0x%X "
"and nv: 0x%X) for first part (header + SMART signature)",
TARGETING::get_huid(iv_dimm), hostCksm, nvCksm);
/*
*@errortype
*@moduleid UPDATE_IMAGE
*@reasoncode NVDIMM_CHECKSUM_ERROR
*@userdata1 NVDIMM Target Huid
*@userdata2[0:15] Host checksum calculated
*@userdata2[16:31] NV checksum returned
*@userdata2[32:47] size of data for checksum
*@devdesc Checksum failure when transferring region
*@custdesc NVDIMM not updated
*/
l_err = new ERRORLOG::ErrlEntry(
ERRORLOG::ERRL_SEV_PREDICTIVE,
UPDATE_IMAGE_DATA,
NVDIMM_CHECKSUM_ERROR,
TARGETING::get_huid(iv_dimm),
FOUR_UINT16_TO_UINT64(
hostCksm, nvCksm,
header_plus_signature_size,
0x0000),
ERRORLOG::ErrlEntry::NO_SW_CALLOUT );
l_err->collectTrace( NVDIMM_COMP_NAME, 256 );
// maybe some data was altered on the NV controller
l_err->addPartCallout( iv_dimm,
HWAS::NV_CONTROLLER_PART_TYPE,
HWAS::SRCI_PRIORITY_HIGH );
// possible code issue
l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE,
HWAS::SRCI_PRIORITY_LOW );
break;
}
// 8. Command the module to validate that the firmware image is valid
// for the module based on the header.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 8");
l_err = validateFwHeader();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: firmware header "
"for 0x%.8X nvdimm cannot be validated",
TARGETING::get_huid(iv_dimm));
break;
}
else
{
TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X "
"updated with valid header + SMART signature",
TARGETING::get_huid(iv_dimm));
}
// 9. Commit the first firmware data region
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 9");
l_err = commitFwRegion();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: commitFwRegion() "
"of first data region failed for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
// 10. Send and commit the remaining firmware data in
// REGION_BLOCK_SIZE regions
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10");
l_err = updateImageData(i_lidImage);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"updateImageData() failed sending full image for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
// 11. Validate the firmware data
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 11");
l_err = validateFwImage();
if ( l_err )
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"validateFwImage() failed for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
else
{
TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X "
"updated with valid image data", TARGETING::get_huid(iv_dimm));
}
// 12. Disable firmware update mode
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 12");
l_fw_update_mode_enabled = false; // don't retry the disable on error
l_err = changeFwUpdateMode(FW_UPDATE_MODE_DISABLED);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"Unable to disable FW update mode for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
// There are two slots for firmware.
// Slot 0 is read-only hence this procedure only updates slot 1.
// At the end of the update, we should explicitly select slot 1,
// by writing 0x1 to FW_SLOT_INFO, to make sure the module is
// running on the latest code.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: Switch to slot 1");
l_err = nvdimmWriteReg(iv_dimm, FW_SLOT_INFO, 0x01);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"Unable to switch to slot 1 for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
// Reset controller to activate new firmware
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: resetController");
l_err = nvdimmResetController(iv_dimm);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"Unable to activate new firmware for 0x%.8X nvdimm",
TARGETING::get_huid(iv_dimm));
break;
}
// force a recollect of the version of code installed
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: verify new code version running");
uint16_t new_version = INVALID_VERSION;
l_err = getVersion(new_version, true);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"Unable to verify NVDIMM 0x%.8X was successfully updated",
TARGETING::get_huid(iv_dimm));
break;
}
else
{
TRACFCOMP(g_trac_nvdimm_upd, "updateImage: NVDIMM 0x%.8X of type "
"0x%04X%04X now running new code level 0x%04X",
TARGETING::get_huid(iv_dimm),
le16toh(iv_manufacturer_id), le16toh(iv_product_id),
le16toh(new_version));
}
} while (0);
// If update operation is aborted, we need to disable update mode
if (l_fw_update_mode_enabled)
{
TRACFCOMP(g_trac_nvdimm_upd, "updateImage: update was aborted, so disable FW_UPDATE_MODE");
errlHndl_t l_err2 = changeFwUpdateMode(FW_UPDATE_MODE_DISABLED);
if (l_err2)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK "updateImage: "
"Attempt to disable Firmware Update Mode of 0x%.8X failed,"
" RC=0x%X", TARGETING::get_huid(iv_dimm),
ERRL_GETRC_SAFE(l_err2));
// Should always have an error here, so link the two errors together
// Just a safety-check so we don't dereference a nullptr
if (l_err)
{
l_err2->plid(l_err->plid());
l_err2->collectTrace(NVDIMM_COMP_NAME, 256);
l_err2->collectTrace(NVDIMM_UPD, 256);
errlCommit(l_err2, NVDIMM_COMP_ID);
}
else
{
// this path shouldn't get run
l_err = l_err2;
}
}
}
return l_err;
}
// HELPER FUNCTIONS FOR UPDATE
errlHndl_t NvdimmInstalledImage::updateImageData(NvdimmLidImage * i_lidImage)
{
errlHndl_t l_err = nullptr;
do
{
uint8_t blocks_per_region;
l_err = getBlocksPerRegion(blocks_per_region);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"getBlocksPerRegion() failed on NVDIMM 0x%.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// calculate the region size which equals block size * blocks per region
uint16_t region_size = BYTES_PER_BLOCK * blocks_per_region;
const uint8_t * fw_img_data =
reinterpret_cast<const uint8_t*>(i_lidImage->getFlashImage());
size_t fw_img_data_len = i_lidImage->getFlashImageSize();
// Get the number of regions required for the amount of data.
// This sets the upper bound for the REGION_ID
uint16_t fw_img_total_regions = fw_img_data_len/region_size;
if (fw_img_data_len % region_size > 0)
{
// account for a partial region
fw_img_total_regions++;
}
if (fw_img_total_regions == 0)
{
/*
*@errortype
*@moduleid UPDATE_IMAGE_DATA
*@reasoncode NVDIMM_ZERO_TOTAL_REGIONS
*@userdata1 NVDIMM Target Huid
*@userdata2[0:15] Firmware image size
*@userdata2[16:31] region_size
*@devdesc Firmware image size is not large enough
* (needs to be at least region_size)
*@custdesc NVDIMM not updated
*/
l_err = new ERRORLOG::ErrlEntry(
ERRORLOG::ERRL_SEV_PREDICTIVE,
UPDATE_IMAGE_DATA,
NVDIMM_ZERO_TOTAL_REGIONS,
TARGETING::get_huid(iv_dimm),
TWO_UINT16_ONE_UINT32_TO_UINT64(
fw_img_data_len,
region_size,
0x00000000),
ERRORLOG::ErrlEntry::ADD_SW_CALLOUT );
l_err->collectTrace( NVDIMM_COMP_NAME, 256 );
break;
}
// 10.a Write the BLOCK_ID, REGION_ID0 and REGION_ID1 registers
// with the appropriate value starting at 0
TRACUCOMP(g_trac_nvdimm_upd,
"updateImage: Sending %d total regions of size %d bytes (total size: 0x%08X)",
fw_img_total_regions, region_size, fw_img_data_len);
l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Write of 0x00 to REGION_ID0 failed on NVDIMM 0x%.8X",
TARGETING::get_huid(iv_dimm));
break;
}
l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Write of 0x00 to REGION_ID1 failed on NVDIMM 0x%.8X",
TARGETING::get_huid(iv_dimm));
break;
}
uint16_t region = 0;
while (region < fw_img_total_regions)
{
if (region % 100 == 0)
{
TRACFCOMP(g_trac_nvdimm_upd,
"updateImage: progress code for sending region %d",
region);
INITSERVICE::sendProgressCode();
}
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.a - region 0x%04X",
region);
// For each region, start with BLOCK_ID of 0. BLOCK_ID
// is controlled in byteRegionBlockTransfer().
l_err = nvdimmWriteReg(iv_dimm, BLOCK_ID, 0x00);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Write of 0x00 to BLOCK_ID failed on NVDIMM 0x%.8X",
TARGETING::get_huid(iv_dimm));
break;
}
// Update REGION_ID0(lsb) and REGION_ID1(msb)
uint8_t l_data = region & 0x00FF;
l_err = nvdimmWriteReg(iv_dimm, REGION_ID0, l_data);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Write of 0x%02X to REGION_ID0 failed on NVDIMM 0x%.8X",
l_data, TARGETING::get_huid(iv_dimm));
break;
}
l_data = (((region & 0xFF00) >> 8) & 0x00FF);
l_err = nvdimmWriteReg(iv_dimm, REGION_ID1, l_data);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d write of 0x%02X to REGION_ID1 failed on NVDIMM "
"0x%.8X", region, l_data, TARGETING::get_huid(iv_dimm));
break;
}
// 10.b Clear the firmware data block to ensure there is no
// residual data.
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.b");
l_err = clearFwDataBlock();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d: clearFwDataBlock() failed on NVDIMM 0x%.8X",
region, TARGETING::get_huid(iv_dimm));
break;
}
// 10.c Send the data over to nvdimm region by region.
// Check if the remaining data is in the multiple of the region size
// If not, have the function to calculate the actual amount of data
// to send over to the nvdimm
const uint8_t * pImageData = &(fw_img_data[(region*region_size)]);
uint16_t data_len = region_size;
if ((fw_img_data_len-(region*region_size)) < region_size)
{
// send a final partial region
data_len = fw_img_data_len-(region*region_size);
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.c - send partial region (%d length)", data_len);
l_err = byteRegionBlockTransfer(pImageData, data_len, false);
}
else
{
// send a full region worth of data
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.c - send full region (%d length)", data_len);
l_err = byteRegionBlockTransfer(pImageData, data_len, true);
}
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d: byteRegionBlockTransfer() failed on NVDIMM 0x%.8X",
region, TARGETING::get_huid(iv_dimm));
break;
}
// 10.d-e After transferring each region, validate the transfer by checksum
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.d-e");
uint16_t hostCksm = crc16(pImageData, data_len);
l_err = clearFwOpsStatus();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d: clearFwOpsStatus() failed on NVDIMM 0x%.8X",
region, TARGETING::get_huid(iv_dimm));
break;
}
uint16_t nvCksm;
l_err = calcAndGetCksm(nvCksm);
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d: calcAndGetCksm() failed on NVDIMM 0x%.8X",
region, TARGETING::get_huid(iv_dimm));
break;
}
if (hostCksm != nvCksm)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d of NVDIMM 0x%.8X: data checksums mismatch "
"(calc host: 0x%X and nv: 0x%X)",
region, TARGETING::get_huid(iv_dimm), hostCksm, nvCksm);
/*
*@errortype
*@moduleid UPDATE_IMAGE_DATA
*@reasoncode NVDIMM_CHECKSUM_ERROR
*@userdata1 NVDIMM Target Huid
*@userdata2[0:15] Host checksum calculated
*@userdata2[16:31] NV checksum returned
*@userdata2[32:47] size of data for checksum
*@userdata2[48:63] region
*@devdesc Checksum failure when transferring region
*@custdesc NVDIMM not updated
*/
l_err = new ERRORLOG::ErrlEntry(
ERRORLOG::ERRL_SEV_PREDICTIVE,
UPDATE_IMAGE_DATA,
NVDIMM_CHECKSUM_ERROR,
TARGETING::get_huid(iv_dimm),
FOUR_UINT16_TO_UINT64(
hostCksm, nvCksm,
region, data_len),
ERRORLOG::ErrlEntry::NO_SW_CALLOUT );
l_err->collectTrace( NVDIMM_COMP_NAME, 256 );
l_err->addPartCallout( iv_dimm,
HWAS::NV_CONTROLLER_PART_TYPE,
HWAS::SRCI_PRIORITY_HIGH );
l_err->addProcedureCallout( HWAS::EPUB_PRC_HB_CODE,
HWAS::SRCI_PRIORITY_LOW );
break;
}
// 10.f Commit the firmware data region transferred
TRACUCOMP(g_trac_nvdimm_upd, "updateImage: step 10.f");
l_err = commitFwRegion();
if (l_err)
{
TRACFCOMP(g_trac_nvdimm_upd, ERR_MRK"updateImageData: "
"Region %d: commitFwRegion() failed on NVDIMM 0x%.8X",
region, TARGETING::get_huid(iv_dimm));
break;
}
region++;
} // End of FW image data transfer
} while (0);
return l_err;
}
errlHndl_t NvdimmInstalledImage::changeFwUpdateMode(fw_update_mode i_mode)
{
errlHndl_t l_err = nullptr;
l_err = nvdimmWriteReg(iv_dimm, FIRMWARE_OPS_CMD, i_mode);
if (!l_err)
{
// Wait for ops to complete
l_err = waitFwOpsComplete();
if (!l_err)
{
// if ops completed successfully, check the status
// to make sure that FW update mode has been enabled/disabled
fw_ops_status_t opStatus;
l_err = nvdimmReadReg(iv_dimm, FIRMWARE_OPS_STATUS, opStatus.whole);
if (!l_err)
{
// Create an error if the mode was NOT set correctly
if (!(((i_mode == FW_UPDATE_MODE_ENABLED) &&
(opStatus.fw_ops_update_mode == 1)) ||
((i_mode == FW_UPDATE_MODE_DISABLED) &&
(opStatus.fw_ops_update_mode == 0))) )
{
/*
*@errortype
*@moduleid CHANGE_FW_UPDATE_MODE
*@reasoncode NVDIMM_UPDATE_MODE_UNCHANGED
*@userdata1 NVDIMM Target Huid
*@userdata2[0:7] Mode setting