1 #include "nugget_tools.h"
2
3 #include <app_nugget.h>
4 #include <nos/NuggetClient.h>
5
6 #include <chrono>
7 #include <cinttypes>
8 #include <cstring>
9 #include <iostream>
10 #include <thread>
11 #include <vector>
12
13 #ifdef ANDROID
14 #include <android-base/endian.h>
15 #include "nos/CitadeldProxyClient.h"
16 #else
17 #include "gflags/gflags.h"
18
19 DEFINE_string(nos_core_serial, "", "USB device serial number to open");
20 #endif // ANDROID
21
22 #ifndef LOG
23 #define LOG(x) std::cerr << __FILE__ << ":" << __LINE__ << " " << #x << ": "
24 #endif // LOG
25
26 using std::chrono::duration;
27 using std::chrono::duration_cast;
28 using std::chrono::high_resolution_clock;
29 using std::chrono::microseconds;
30 using std::string;
31
32 namespace nugget_tools {
33
GetCitadelUSBSerialNo()34 std::string GetCitadelUSBSerialNo() {
35 #ifdef ANDROID
36 return "";
37 #else
38 if (FLAGS_nos_core_serial.empty()) {
39 const char *env_default = secure_getenv("CITADEL_DEVICE");
40 if (env_default && *env_default) {
41 FLAGS_nos_core_serial.assign(env_default);
42 std::cerr << "Using CITADEL_DEVICE=" << FLAGS_nos_core_serial << "\n";
43 }
44 }
45 return FLAGS_nos_core_serial;
46 #endif
47 }
48
MakeNuggetClient()49 std::unique_ptr<nos::NuggetClientInterface> MakeNuggetClient() {
50 #ifdef ANDROID
51 std::unique_ptr<nos::NuggetClientInterface> client =
52 std::unique_ptr<nos::NuggetClientInterface>(new nos::NuggetClient());
53 client->Open();
54 if (!client->IsOpen()) {
55 client = std::unique_ptr<nos::NuggetClientInterface>(
56 new nos::CitadeldProxyClient());
57 }
58 return client;
59 #else
60 return std::unique_ptr<nos::NuggetClientInterface>(
61 new nos::NuggetClient(GetCitadelUSBSerialNo()));
62 #endif
63 }
64
CyclesSinceBoot(nos::NuggetClientInterface * client,uint32_t * cycles)65 bool CyclesSinceBoot(nos::NuggetClientInterface *client, uint32_t *cycles) {
66 std::vector<uint8_t> buffer;
67 buffer.reserve(sizeof(uint32_t));
68 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_CYCLES_SINCE_BOOT,
69 buffer, &buffer) != app_status::APP_SUCCESS) {
70 perror("test");
71 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_CYCLES_SINCE_BOOT, ...) failed!\n";
72 return false;
73 };
74 if (buffer.size() != sizeof(uint32_t)) {
75 LOG(ERROR) << "Unexpected size of cycle count!\n";
76 return false;
77 }
78 *cycles = le32toh(*reinterpret_cast<uint32_t *>(buffer.data()));
79 return true;
80 }
81
ShowStats(const char * msg,const struct nugget_app_low_power_stats & stats)82 static void ShowStats(const char *msg,
83 const struct nugget_app_low_power_stats& stats) {
84 printf("%s\n", msg);
85 printf(" hard_reset_count %" PRIu64 "\n", stats.hard_reset_count);
86 printf(" time_since_hard_reset %" PRIu64 "\n",
87 stats.time_since_hard_reset);
88 printf(" wake_count %" PRIu64 "\n", stats.wake_count);
89 printf(" time_at_last_wake %" PRIu64 "\n", stats.time_at_last_wake);
90 printf(" time_spent_awake %" PRIu64 "\n", stats.time_spent_awake);
91 printf(" deep_sleep_count %" PRIu64 "\n", stats.deep_sleep_count);
92 printf(" time_at_last_deep_sleep %" PRIu64 "\n",
93 stats.time_at_last_deep_sleep);
94 printf(" time_spent_in_deep_sleep %" PRIu64 "\n",
95 stats.time_spent_in_deep_sleep);
96 }
97
RebootNugget(nos::NuggetClientInterface * client)98 bool RebootNugget(nos::NuggetClientInterface *client) {
99 struct nugget_app_low_power_stats stats0;
100 struct nugget_app_low_power_stats stats1;
101 std::vector<uint8_t> buffer;
102
103 // Grab stats before sleeping
104 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
105 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
106 buffer, &buffer) != app_status::APP_SUCCESS) {
107 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
108 return false;
109 }
110 memcpy(&stats0, buffer.data(), sizeof(stats0));
111
112 // Capture the time here to allow for some tolerance on the reported time.
113 auto start = high_resolution_clock::now();
114
115 // Tell Nugget OS to reboot
116 std::vector<uint8_t> ignored;
117 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_REBOOT, ignored,
118 nullptr) != app_status::APP_SUCCESS) {
119 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_REBOOT, ...) failed!\n";
120 return false;
121 }
122
123 // Grab stats after sleeping
124 buffer.empty();
125 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
126 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
127 buffer, &buffer) != app_status::APP_SUCCESS) {
128 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
129 return false;
130 }
131 memcpy(&stats1, buffer.data(), sizeof(stats1));
132
133 // Figure a max elapsed time that Nugget OS should see (our time + 5%).
134 auto max_usecs =
135 duration_cast<microseconds>(high_resolution_clock::now() - start) *
136 105 / 100;
137
138 // Verify that Citadel rebooted
139 if (stats1.hard_reset_count == stats0.hard_reset_count + 1 &&
140 stats1.time_at_last_wake == 0 &&
141 stats1.deep_sleep_count == 0 &&
142 std::chrono::microseconds(stats1.time_since_hard_reset) < max_usecs) {
143 return true;
144 }
145
146 LOG(ERROR) << "Citadel didn't reboot within "
147 << max_usecs.count() << " microseconds\n";
148 ShowStats("stats before waiting", stats0);
149 ShowStats("stats after waiting", stats1);
150
151 return false;
152 }
153
WaitForSleep(nos::NuggetClientInterface * client,uint32_t * seconds_waited)154 bool WaitForSleep(nos::NuggetClientInterface *client, uint32_t *seconds_waited) {
155 struct nugget_app_low_power_stats stats0;
156 struct nugget_app_low_power_stats stats1;
157 std::vector<uint8_t> buffer;
158
159 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
160 // Grab stats before sleeping
161 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
162 buffer, &buffer) != app_status::APP_SUCCESS) {
163 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
164 return false;
165 }
166 memcpy(&stats0, buffer.data(), sizeof(stats0));
167
168 // Wait for Citadel to fall asleep
169 constexpr uint32_t wait_seconds = 4;
170 std::this_thread::sleep_for(std::chrono::seconds(wait_seconds));
171
172 // Grab stats after sleeping
173 buffer.empty();
174 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
175 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
176 buffer, &buffer) != app_status::APP_SUCCESS) {
177 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
178 return false;
179 }
180 memcpy(&stats1, buffer.data(), sizeof(stats1));
181
182 // Verify that Citadel went to sleep but didn't reboot
183 if (stats1.hard_reset_count == stats0.hard_reset_count &&
184 stats1.deep_sleep_count == stats0.deep_sleep_count + 1 &&
185 stats1.wake_count == stats0.wake_count + 1 &&
186 stats1.time_spent_in_deep_sleep > stats0.time_spent_in_deep_sleep) {
187 // Yep, looks good
188 if (seconds_waited) {
189 *seconds_waited = wait_seconds;
190 }
191 return true;
192 }
193
194 LOG(ERROR) << "Citadel didn't sleep\n";
195 ShowStats("stats before waiting", stats0);
196 ShowStats("stats after waiting", stats1);
197
198 return false;
199 }
200
WipeUserData(nos::NuggetClientInterface * client)201 bool WipeUserData(nos::NuggetClientInterface *client) {
202 struct nugget_app_low_power_stats stats0;
203 struct nugget_app_low_power_stats stats1;
204 std::vector<uint8_t> buffer;
205
206 // Grab stats before sleeping
207 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
208 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
209 buffer, &buffer) != app_status::APP_SUCCESS) {
210 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
211 return false;
212 }
213 memcpy(&stats0, buffer.data(), sizeof(stats0));
214
215 // Request wipe of user data which should hard reboot
216 buffer.resize(4);
217 *reinterpret_cast<uint32_t *>(buffer.data()) = htole32(ERASE_CONFIRMATION);
218 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_NUKE_FROM_ORBIT,
219 buffer, nullptr) != app_status::APP_SUCCESS) {
220 return false;
221 }
222
223 // Grab stats after sleeping
224 buffer.empty();
225 buffer.reserve(sizeof(struct nugget_app_low_power_stats));
226 if (client->CallApp(APP_ID_NUGGET, NUGGET_PARAM_GET_LOW_POWER_STATS,
227 buffer, &buffer) != app_status::APP_SUCCESS) {
228 LOG(ERROR) << "CallApp(..., NUGGET_PARAM_GET_LOW_POWER_STATS, ...) failed!\n";
229 return false;
230 }
231 memcpy(&stats1, buffer.data(), sizeof(stats1));
232
233 // Verify that Citadel didn't reset
234 const bool ret = stats1.hard_reset_count == stats0.hard_reset_count;
235 if (!ret) {
236 LOG(ERROR) << "Citadel reset while wiping user data\n";
237 }
238 return ret;
239 }
240
241 } // namespace nugget_tools
242