Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @brief Defines a table of fabrics that have provisioned the device.
20 : */
21 :
22 : #include "LastKnownGoodTime.h"
23 :
24 : #include <lib/support/BufferWriter.h>
25 : #include <lib/support/DefaultStorageKeyAllocator.h>
26 : #include <lib/support/SafeInt.h>
27 : #include <platform/ConfigurationManager.h>
28 :
29 : namespace chip {
30 :
31 : namespace {
32 : // Tags for Last Known Good Time.
33 : constexpr TLV::Tag kLastKnownGoodChipEpochSecondsTag = TLV::ContextTag(0);
34 : } // anonymous namespace
35 :
36 3433 : void LastKnownGoodTime::LogTime(const char * msg, System::Clock::Seconds32 chipEpochTime)
37 : {
38 : #if CHIP_PROGRESS_LOGGING
39 3433 : char buf[26] = { 0 }; // strlen("00000-000-000T000:000:000") == 25
40 : uint16_t year;
41 : uint8_t month;
42 : uint8_t day;
43 : uint8_t hour;
44 : uint8_t minute;
45 : uint8_t second;
46 3433 : ChipEpochToCalendarTime(chipEpochTime.count(), year, month, day, hour, minute, second);
47 3433 : snprintf(buf, sizeof(buf), "%04u-%02u-%02uT%02u:%02u:%02u", year, month, day, hour, minute, second);
48 3433 : ChipLogProgress(TimeService, "%s%s", StringOrNullMarker(msg), buf);
49 : #else
50 : IgnoreUnusedVariable(msg);
51 : IgnoreUnusedVariable(chipEpochTime);
52 : #endif
53 3433 : }
54 :
55 835 : CHIP_ERROR LastKnownGoodTime::LoadLastKnownGoodChipEpochTime(System::Clock::Seconds32 & lastKnownGoodChipEpochTime) const
56 : {
57 : uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
58 835 : uint16_t size = sizeof(buf);
59 : uint32_t seconds;
60 835 : ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf, size));
61 445 : TLV::ContiguousBufferTLVReader reader;
62 445 : reader.Init(buf, size);
63 445 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
64 : TLV::TLVType containerType;
65 445 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
66 445 : ReturnErrorOnFailure(reader.Next(kLastKnownGoodChipEpochSecondsTag));
67 445 : ReturnErrorOnFailure(reader.Get(seconds));
68 445 : lastKnownGoodChipEpochTime = System::Clock::Seconds32(seconds);
69 445 : return CHIP_NO_ERROR;
70 : }
71 :
72 1096 : CHIP_ERROR LastKnownGoodTime::StoreLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime) const
73 : {
74 : uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
75 1096 : TLV::TLVWriter writer;
76 1096 : writer.Init(buf);
77 : TLV::TLVType outerType;
78 1096 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
79 1096 : ReturnErrorOnFailure(writer.Put(kLastKnownGoodChipEpochSecondsTag, lastKnownGoodChipEpochTime.count()));
80 1096 : ReturnErrorOnFailure(writer.EndContainer(outerType));
81 1096 : const auto length = writer.GetLengthWritten();
82 1096 : VerifyOrReturnError(CanCastTo<uint16_t>(length), CHIP_ERROR_BUFFER_TOO_SMALL);
83 1096 : ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf,
84 : static_cast<uint16_t>(length)));
85 1096 : return CHIP_NO_ERROR;
86 : }
87 :
88 407 : CHIP_ERROR LastKnownGoodTime::Init(PersistentStorageDelegate * storage)
89 : {
90 407 : CHIP_ERROR err = CHIP_NO_ERROR;
91 407 : mStorage = storage;
92 : // 3.5.6.1 Last Known Good UTC Time:
93 : //
94 : // "A Node’s initial out-of-box Last Known Good UTC time SHALL be the
95 : // compile-time of the firmware."
96 : System::Clock::Seconds32 buildTime;
97 407 : SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime));
98 : System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
99 407 : err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime);
100 407 : VerifyOrExit(err == CHIP_NO_ERROR || err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND, ;);
101 407 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
102 : {
103 388 : ChipLogProgress(TimeService, "Last Known Good Time: [unknown]");
104 : }
105 : else
106 : {
107 19 : LogTime("Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
108 : }
109 407 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND || buildTime > storedLastKnownGoodChipEpochTime)
110 : {
111 : // If we have no value in persistence, or the firmware build time is
112 : // later than the value in persistence, set last known good time to the
113 : // firmware build time and write back.
114 388 : LogTime("Setting Last Known Good Time to firmware build time ", buildTime);
115 388 : mLastKnownGoodChipEpochTime.SetValue(buildTime);
116 388 : SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(buildTime));
117 : }
118 : else
119 : {
120 19 : mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
121 : }
122 407 : exit:
123 407 : if (err != CHIP_NO_ERROR)
124 : {
125 0 : ChipLogError(TimeService, "Failed to init Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
126 : }
127 407 : return err;
128 : }
129 :
130 8 : CHIP_ERROR LastKnownGoodTime::SetLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime,
131 : System::Clock::Seconds32 notBefore)
132 : {
133 8 : CHIP_ERROR err = CHIP_NO_ERROR;
134 8 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
135 8 : LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
136 8 : LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime);
137 :
138 : // Verify that the passed value is not earlier than the firmware build time.
139 : System::Clock::Seconds32 buildTime;
140 8 : SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime));
141 8 : VerifyOrExit(lastKnownGoodChipEpochTime >= buildTime, err = CHIP_ERROR_INVALID_ARGUMENT);
142 : // Verify that the passed value is not earlier than the passed NotBefore time.
143 5 : VerifyOrExit(lastKnownGoodChipEpochTime >= notBefore, err = CHIP_ERROR_INVALID_ARGUMENT);
144 :
145 : // Passed value is valid. Capture it and write back to persistence.
146 : //
147 : // Note that we are purposefully overwriting any previous last known
148 : // good time that may have been stored as part of a fail-safe context.
149 : // This is intentional: we don't promise not to change last known good
150 : // time during the fail-safe timer. For instance, if the platform has a
151 : // new, better time source, it is legal to capture it. If we do, we should
152 : // both overwrite last known good time and discard the previous value stored
153 : // for fail safe recovery, as together these comprise a transaction. By
154 : // overwriting both, we are fully superseding that transaction with our
155 : // own, which does not to have a revert feature.
156 4 : SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime));
157 4 : mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime);
158 8 : exit:
159 8 : if (err != CHIP_NO_ERROR)
160 : {
161 4 : ChipLogError(TimeService, "Failed to update Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
162 : }
163 : else
164 : {
165 4 : LogTime("Updating Last Known Good Time to ", lastKnownGoodChipEpochTime);
166 : }
167 8 : return err;
168 : }
169 :
170 715 : CHIP_ERROR LastKnownGoodTime::UpdatePendingLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime)
171 : {
172 715 : CHIP_ERROR err = CHIP_NO_ERROR;
173 715 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
174 715 : LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
175 715 : LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime);
176 715 : if (lastKnownGoodChipEpochTime > mLastKnownGoodChipEpochTime.Value())
177 : {
178 18 : LogTime("Updating pending Last Known Good Time to ", lastKnownGoodChipEpochTime);
179 18 : mLastKnownGoodChipEpochTime.SetValue(lastKnownGoodChipEpochTime);
180 : }
181 : else
182 : {
183 697 : ChipLogProgress(TimeService, "Retaining current Last Known Good Time");
184 : }
185 0 : exit:
186 715 : return err;
187 : }
188 :
189 704 : CHIP_ERROR LastKnownGoodTime::CommitPendingLastKnownGoodChipEpochTime()
190 : {
191 704 : CHIP_ERROR err = CHIP_NO_ERROR;
192 704 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
193 704 : LogTime("Committing Last Known Good Time to storage: ", mLastKnownGoodChipEpochTime.Value());
194 704 : SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(mLastKnownGoodChipEpochTime.Value()));
195 704 : exit:
196 704 : if (err != CHIP_NO_ERROR)
197 : {
198 0 : ChipLogError(TimeService, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
199 : }
200 704 : return err;
201 : }
202 :
203 428 : CHIP_ERROR LastKnownGoodTime::RevertPendingLastKnownGoodChipEpochTime()
204 : {
205 428 : CHIP_ERROR err = CHIP_NO_ERROR;
206 : System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
207 428 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
208 428 : LogTime("Pending Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
209 428 : SuccessOrExit(err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime));
210 426 : LogTime("Previous Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
211 426 : mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
212 428 : exit:
213 428 : if (err != CHIP_NO_ERROR)
214 : {
215 : // We do not expect to arrive here unless the kvstore broke or some
216 : // other code removed our value from persistence. However, clearing the
217 : // in-memory value is the right thing to do for all cases. This will
218 : // prevent other code from consuming an invalid time.
219 2 : ChipLogError(TimeService,
220 : "Clearing Last Known Good Time; failed to load a previous value from persistence: %" CHIP_ERROR_FORMAT,
221 : err.Format());
222 2 : mLastKnownGoodChipEpochTime.ClearValue();
223 : }
224 : else
225 : {
226 426 : ChipLogProgress(TimeService, "Reverted Last Known Good Time to previous value");
227 : }
228 428 : return err;
229 : }
230 :
231 : } // namespace chip
|