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