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 3088 : void LastKnownGoodTime::LogTime(const char * msg, System::Clock::Seconds32 chipEpochTime)
37 : {
38 : #if CHIP_PROGRESS_LOGGING
39 3088 : 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 3088 : ChipEpochToCalendarTime(chipEpochTime.count(), year, month, day, hour, minute, second);
47 3088 : snprintf(buf, sizeof(buf), "%04u-%02u-%02uT%02u:%02u:%02u", year, month, day, hour, minute, second);
48 3088 : ChipLogProgress(TimeService, "%s%s", StringOrNullMarker(msg), buf);
49 : #else
50 : IgnoreUnusedVariable(msg);
51 : IgnoreUnusedVariable(chipEpochTime);
52 : #endif
53 3088 : }
54 :
55 755 : CHIP_ERROR LastKnownGoodTime::LoadLastKnownGoodChipEpochTime(System::Clock::Seconds32 & lastKnownGoodChipEpochTime) const
56 : {
57 : uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
58 755 : uint16_t size = sizeof(buf);
59 : uint32_t seconds;
60 755 : ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf, size));
61 405 : TLV::ContiguousBufferTLVReader reader;
62 405 : reader.Init(buf, size);
63 405 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag()));
64 : TLV::TLVType containerType;
65 405 : ReturnErrorOnFailure(reader.EnterContainer(containerType));
66 405 : ReturnErrorOnFailure(reader.Next(kLastKnownGoodChipEpochSecondsTag));
67 405 : ReturnErrorOnFailure(reader.Get(seconds));
68 405 : lastKnownGoodChipEpochTime = System::Clock::Seconds32(seconds);
69 405 : return CHIP_NO_ERROR;
70 : }
71 :
72 981 : CHIP_ERROR LastKnownGoodTime::StoreLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime) const
73 : {
74 : uint8_t buf[LastKnownGoodTimeTLVMaxSize()];
75 981 : TLV::TLVWriter writer;
76 981 : writer.Init(buf);
77 : TLV::TLVType outerType;
78 981 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
79 981 : ReturnErrorOnFailure(writer.Put(kLastKnownGoodChipEpochSecondsTag, lastKnownGoodChipEpochTime.count()));
80 981 : ReturnErrorOnFailure(writer.EndContainer(outerType));
81 981 : const auto length = writer.GetLengthWritten();
82 981 : VerifyOrReturnError(CanCastTo<uint16_t>(length), CHIP_ERROR_BUFFER_TOO_SMALL);
83 981 : ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::LastKnownGoodTimeKey().KeyName(), buf,
84 : static_cast<uint16_t>(length)));
85 981 : return CHIP_NO_ERROR;
86 : }
87 :
88 367 : CHIP_ERROR LastKnownGoodTime::Init(PersistentStorageDelegate * storage)
89 : {
90 367 : CHIP_ERROR err = CHIP_NO_ERROR;
91 367 : 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 367 : SuccessOrExit(err = DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(buildTime));
98 : System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
99 367 : err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime);
100 367 : VerifyOrExit(err == CHIP_NO_ERROR || err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND, ;);
101 367 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
102 : {
103 348 : ChipLogProgress(TimeService, "Last Known Good Time: [unknown]");
104 : }
105 : else
106 : {
107 19 : LogTime("Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
108 : }
109 367 : 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 348 : LogTime("Setting Last Known Good Time to firmware build time ", buildTime);
115 348 : mLastKnownGoodChipEpochTime.SetValue(buildTime);
116 348 : SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(buildTime));
117 : }
118 : else
119 : {
120 19 : mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
121 : }
122 367 : exit:
123 367 : if (err != CHIP_NO_ERROR)
124 : {
125 0 : ChipLogError(TimeService, "Failed to init Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
126 : }
127 367 : 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 640 : CHIP_ERROR LastKnownGoodTime::UpdatePendingLastKnownGoodChipEpochTime(System::Clock::Seconds32 lastKnownGoodChipEpochTime)
171 : {
172 640 : CHIP_ERROR err = CHIP_NO_ERROR;
173 640 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
174 640 : LogTime("Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
175 640 : LogTime("New proposed Last Known Good Time: ", lastKnownGoodChipEpochTime);
176 640 : 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 622 : ChipLogProgress(TimeService, "Retaining current Last Known Good Time");
184 : }
185 0 : exit:
186 640 : return err;
187 : }
188 :
189 629 : CHIP_ERROR LastKnownGoodTime::CommitPendingLastKnownGoodChipEpochTime()
190 : {
191 629 : CHIP_ERROR err = CHIP_NO_ERROR;
192 629 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
193 629 : LogTime("Committing Last Known Good Time to storage: ", mLastKnownGoodChipEpochTime.Value());
194 629 : SuccessOrExit(err = StoreLastKnownGoodChipEpochTime(mLastKnownGoodChipEpochTime.Value()));
195 629 : exit:
196 629 : if (err != CHIP_NO_ERROR)
197 : {
198 0 : ChipLogError(TimeService, "Failed to commit Last Known Good Time: %" CHIP_ERROR_FORMAT, err.Format());
199 : }
200 629 : return err;
201 : }
202 :
203 388 : CHIP_ERROR LastKnownGoodTime::RevertPendingLastKnownGoodChipEpochTime()
204 : {
205 388 : CHIP_ERROR err = CHIP_NO_ERROR;
206 : System::Clock::Seconds32 storedLastKnownGoodChipEpochTime;
207 388 : VerifyOrExit(mLastKnownGoodChipEpochTime.HasValue(), err = CHIP_ERROR_INCORRECT_STATE);
208 388 : LogTime("Pending Last Known Good Time: ", mLastKnownGoodChipEpochTime.Value());
209 388 : SuccessOrExit(err = LoadLastKnownGoodChipEpochTime(storedLastKnownGoodChipEpochTime));
210 386 : LogTime("Previous Last Known Good Time: ", storedLastKnownGoodChipEpochTime);
211 386 : mLastKnownGoodChipEpochTime.SetValue(storedLastKnownGoodChipEpochTime);
212 388 : exit:
213 388 : 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 386 : ChipLogProgress(TimeService, "Reverted Last Known Good Time to previous value");
227 : }
228 388 : return err;
229 : }
230 :
231 : } // namespace chip
|