1 /*
2  * Copyright (c) 2018-2022, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 /* NOTE: For the security of the protected storage system, the bootloader
9  * rollback protection, and the protection of cryptographic material  it is
10  * CRITICAL to use a internal (in-die) persistent memory for the implementation
11  * of the OTP_NV_COUNTERS flash area (see flash_otp_nv_layout.c).
12  */
13 
14 #include "tfm_plat_nv_counters.h"
15 
16 #include <limits.h>
17 #include "Driver_Flash.h"
18 #include "flash_layout.h"
19 #include "tfm_plat_otp.h"
20 #include "cmsis_compiler.h"
21 
22 #include <string.h>
23 
24 #define OTP_COUNTER_MAX_SIZE    (128u * sizeof(uint32_t))
25 #define NV_COUNTER_SIZE         4
26 #define OTP_COUNTER_MAGIC       0x3333CAFE
27 
tfm_plat_init_nv_counter(void)28 enum tfm_plat_err_t tfm_plat_init_nv_counter(void)
29 {
30     return TFM_PLAT_ERR_SUCCESS;
31 }
32 
read_nv_counter_otp(enum tfm_otp_element_id_t id,uint8_t * val)33 static enum tfm_plat_err_t read_nv_counter_otp(enum tfm_otp_element_id_t id,
34                                                uint8_t *val)
35 {
36     size_t counter_size;
37     enum tfm_plat_err_t err;
38     size_t idx;
39     uint32_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint32_t)] = {0};
40     uint32_t count;
41 
42     err = tfm_plat_otp_get_size(id, &counter_size);
43     if (err != TFM_PLAT_ERR_SUCCESS) {
44         return err;
45     }
46 
47     counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
48                                                        : counter_size;
49 
50     err = tfm_plat_otp_read(id, counter_size, (uint8_t *)counter_value);
51     if (err != TFM_PLAT_ERR_SUCCESS) {
52         return err;
53     }
54 
55     count = 0;
56     for (idx = 0; idx < counter_size / sizeof(uint32_t); idx++) {
57         if (counter_value[idx] == OTP_COUNTER_MAGIC) {
58             count += 1;
59         } else if (counter_value[idx] == 0) {
60             break;
61         } else {
62             return TFM_PLAT_ERR_SYSTEM_ERR;
63         }
64     }
65 
66     memcpy(val, &count, NV_COUNTER_SIZE);
67 
68     return TFM_PLAT_ERR_SUCCESS;
69 }
70 
tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t size,uint8_t * val)71 enum tfm_plat_err_t tfm_plat_read_nv_counter(enum tfm_nv_counter_t counter_id,
72                                              uint32_t size, uint8_t *val)
73 {
74     if (size != NV_COUNTER_SIZE) {
75         return TFM_PLAT_ERR_SYSTEM_ERR;
76     }
77 
78     switch(counter_id) {
79     case (PLAT_NV_COUNTER_BL2_0):
80         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_0, val);
81     case (PLAT_NV_COUNTER_BL2_1):
82         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_1, val);
83     case (PLAT_NV_COUNTER_BL2_2):
84         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_2, val);
85     case (PLAT_NV_COUNTER_BL2_3):
86         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_3, val);
87 
88     case (PLAT_NV_COUNTER_NS_0):
89         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, val);
90     case (PLAT_NV_COUNTER_NS_1):
91         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, val);
92     case (PLAT_NV_COUNTER_NS_2):
93         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, val);
94 
95     case (PLAT_NV_COUNTER_BL1_0):
96         return read_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, val);
97 
98     default:
99         return TFM_PLAT_ERR_UNSUPPORTED;
100     }
101 }
102 
set_nv_counter_otp(enum tfm_otp_element_id_t id,uint32_t value)103 static enum tfm_plat_err_t set_nv_counter_otp(enum tfm_otp_element_id_t id,
104                                               uint32_t value)
105 {
106     size_t counter_size;
107     enum tfm_plat_err_t err;
108     size_t idx;
109     uint32_t counter_value[OTP_COUNTER_MAX_SIZE / sizeof(uint32_t)] = {0};
110 
111     err = tfm_plat_otp_get_size(id, &counter_size);
112     if (err != TFM_PLAT_ERR_SUCCESS) {
113         return err;
114     }
115 
116     counter_size = counter_size > OTP_COUNTER_MAX_SIZE ? OTP_COUNTER_MAX_SIZE
117                                                        : counter_size;
118 
119     if (value > counter_size) {
120         return TFM_PLAT_ERR_MAX_VALUE;
121     }
122 
123     for (idx = 0; idx < value; idx++) {
124         counter_value[idx] = OTP_COUNTER_MAGIC;
125     }
126 
127     err = tfm_plat_otp_write(id, sizeof(counter_value),
128                              (uint8_t *)counter_value);
129 
130     return err;
131 }
132 
tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,uint32_t value)133 enum tfm_plat_err_t tfm_plat_set_nv_counter(enum tfm_nv_counter_t counter_id,
134                                             uint32_t value)
135 {
136     switch(counter_id) {
137     case (PLAT_NV_COUNTER_BL2_0):
138         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_0, value);
139     case (PLAT_NV_COUNTER_BL2_1):
140         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_1, value);
141     case (PLAT_NV_COUNTER_BL2_2):
142         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_2, value);
143     case (PLAT_NV_COUNTER_BL2_3):
144         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL2_3, value);
145 
146     case (PLAT_NV_COUNTER_NS_0):
147         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_0, value);
148     case (PLAT_NV_COUNTER_NS_1):
149         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_1, value);
150     case (PLAT_NV_COUNTER_NS_2):
151         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_NS_2, value);
152 
153     case (PLAT_NV_COUNTER_BL1_0):
154         return set_nv_counter_otp(PLAT_OTP_ID_NV_COUNTER_BL1_0, value);
155 
156     default:
157         return TFM_PLAT_ERR_UNSUPPORTED;
158     }
159 }
160 
tfm_plat_increment_nv_counter(enum tfm_nv_counter_t counter_id)161 enum tfm_plat_err_t tfm_plat_increment_nv_counter(
162                                            enum tfm_nv_counter_t counter_id)
163 {
164     uint32_t security_cnt;
165     enum tfm_plat_err_t err;
166 
167     err = tfm_plat_read_nv_counter(counter_id,
168                                    sizeof(security_cnt),
169                                    (uint8_t *)&security_cnt);
170     if (err != TFM_PLAT_ERR_SUCCESS) {
171         return err;
172     }
173 
174     if (security_cnt == UINT32_MAX) {
175         return TFM_PLAT_ERR_MAX_VALUE;
176     }
177 
178     return tfm_plat_set_nv_counter(counter_id, security_cnt + 1u);
179 }
180